在線倍增法求LCA專題

1.cojs 186. [USACO Oct08] 牧場旅行

★★   輸入文件:pwalk.in   輸出文件:pwalk.out   簡單對比
時間限制:1 s   內存限制:128 MB
ios

n個被天然地編號爲1..n奶牛(1<=n<=1000)正在一樣被方便的編號爲1..n的n個牧場中吃草。更加天然而方便的是,第i個奶牛就在第i個牧場中吃草。算法

其中的一些對牧場被總共的n-1條雙向通道的一條鏈接。奶牛能夠經過通道。第i條通道鏈接的兩個牧場是A_i和B_i(1<=A_i<=N;1<=B_i<=N)其長度是L_i(1<=L_i<=10000)。flask

通道只會鏈接兩個不一樣的牧場,因此這些通道使得整個牧場構成了一棵樹。數組

奶牛們是好交際的但願可以常常的訪問別的奶牛。急切地,它們但願你能經過告訴它們Q(1<=Q<=1000)對牧場的路徑來幫助他們安排旅行。(這裏將有Q個詢問,p1,p2(1<=p1<=n;1<=p1<=n))app

分數:200spa

問題名稱:pwalkpwa

輸入格式:調試

  • 第1行:兩個用空格隔開的整數:n和Q
  • 第2..n行:第i+1行包含三個用空格隔開的整數:A_i,B_i和L_i
  • 第n+1..N+Q行:每行包含兩個用空格隔開的整數,表明兩個不一樣的牧場,p1和p2

輸入樣例(file pwalk.in):code

4 2
2 1 2
4 3 2
1 4 3
1 2
3 2

輸出格式:blog

  • 第1..Q行:行i包含第i個詢問的答案。

輸出樣例:

2
7

輸出說明:

詢問1:牧場1和牧場2的路徑長度爲2。 詢問2:3->4->1->2;總長爲7。

#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#define Er 20
#define N 1011
int deepp[N],head[N];
int father[N][Er];
struct Edge{
    int v,last,w;
}edge[N<<1];
int t=0,n,Q,ai,bi,li;
int p1,p2;
typedef long long ll;
ll dis[N]={0};
void add_edge(int u,int v,int w)
{
    ++t;
    edge[t].v=v;
    edge[t].w=w;
    edge[t].last=head[u];
    head[u]=t;
}
void input()
{
    scanf("%d%d",&n,&Q);
    for(int i=1;i<=n-1;++i)
    {
        scanf("%d%d%d",&ai,&bi,&li);
        add_edge(ai,bi,li);
        add_edge(bi,ai,li);
    }
}
void dfs(int k)
{
    for(int l=head[k];l;l=edge[l].last)
    {
        if(!deepp[edge[l].v])
        {
            deepp[edge[l].v]=deepp[k]+1;
            dis[edge[l].v]=dis[k]+edge[l].w;
            father[edge[l].v][0]=k;
            dfs(edge[l].v);
        }
    }
}
void pre_chuli()
{
    for(int j=1;(1<<j)<=n;++j)
      for(int i=1;i<=n;++i)
      if(father[i][j-1]!=-1)
      father[i][j]=father[father[i][j-1]][j-1];
}
int lca(int a,int b)
{
    if(deepp[a]<deepp[b]) swap(a,b);
    int i;
    for(i=0;(1<<i)<=deepp[a];++i);
    i--;
    for(int j=i;j>=0;--j)
      if(deepp[a]-deepp[b]>=(1<<j))
         a=father[a][j];
    if(a==b) return a;
    for(int j=i;j>=0;--j)
      if(father[a][j]!=-1&&father[a][j]!=father[b][j])
      {
        a=father[a][j];
        b=father[b][j];
      }    
    return father[a][0];
}
int main()
{
    freopen("pwalk.in","r",stdin);
    freopen("pwalk.out","w",stdout);
    input();
    memset(father,-1,sizeof(father));
    dis[1]=0;
    deepp[1]=1;
    dfs(1);
    pre_chuli();
    for(int i=1;i<=Q;++i)
    {
        scanf("%d%d",&p1,&p2);
        int ance=lca(p1,p2);
        cout<<dis[p1]+dis[p2]-2*dis[ance]<<endl;
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

2.cojs 2075. [ZLXOI2015][異次元聖戰III]ZLX的隕落

★★☆   輸入文件:ThefallingofZLX.in   輸出文件:ThefallingofZLX.out   簡單對比
時間限制:1 s   內存限制:256 MB

【題目描述】

正當革命如火如荼,情侶教會日薄西山之時,SOX和FFF的分歧卻愈來愈大,SOX認爲情侶教會不合適的、無限制的秀恩愛和階級歧視值得反對,而FFF認爲一切情侶都該從這個世界上消失。

SOX先發制人,率先接管了全國政權並突襲了FFF,暗殺了FFF的首領,FFF的紅色革命事業遭到了空前的打擊,一些FFF團員積極抵抗,另外一些FFF團員隱居避世,而一些FFF團員則走向極端,參加了一個邪惡組織:詛咒教會

你雖然對SOX下山摘桃子的行爲不滿,但你對邪教更不滿。在對詛咒教會的長期調查中,你發現該組織的操縱者是亡靈巫師ZLX!如今,維護正義的使命降到了你身上!你和其餘的一些遠征軍將士前往ZLX的城堡,卻掉入了ZLX的魔法陷阱--扭曲虛空,扭曲虛空由n個魔法結界空間組成,m條虛空隧道鏈接着它們,你和其餘的遠征軍將士(剛好有n個)分散在魔法結界空間裏,只有會合在一塊兒,大家才能衝破封鎖(扭曲虛空是一顆樹)。如今,你向平行世界的你提出了疑問,若是給出兩我的會合,總共至少須要多少魔法能量?

已知虛空隧道的長度與消耗的魔法能量在數值上相等。

ZLX的末日已經到臨,等到你衝出虛空隧道,親手結果了ZLX吧!

 

【輸入格式】

第一行一個正整數N,表明魔法結界空間的個數,一個正整數M,表明虛空隧道的個數

接下來M行,每行三個數u,v,w,表明虛空隧道鏈接的兩個點和虛空隧道的長度

接下來一個正整數Q,表明查詢個數

接下來Q行,每行兩個數u,v表明詢問從u到v須要消耗的魔法能量

【輸出格式】

Q行,每行一個正整數

【樣例輸入】

6 5 1 2 7 1 3 3 2 4 5 3 5 7 4 6 6 5 3 4 6 3 5 1 4 3 4 2

【樣例輸出】

15 21 10 15 5

【提示】

對於20%的數據,n<=300,q<=300

對於40%的數據,n<=2000,q<=2000

對於100%的數據,n<=100000,q<=100000,w<=32767,m=n-1

 1 #define N 100100
 2 #include<iostream>
 3 using namespace std;
 4 #include<cstdio>
 5 #include<cstring>
 6 #define Er 20
 7 typedef long long ll;
 8 ll dis[N]={0};
 9 int deepp[N],father[N][Er],t,n,m,u1,v1,w1,Q;
10 struct Edge{
11     int v,last,w;
12 }edge[N<<1];
13 int head[N];
14 void add_edge(int u,int v,int w)
15 {
16     ++t;
17     edge[t].v=v;
18     edge[t].w=w;
19     edge[t].last=head[u];
20     head[u]=t;
21 }
22 void input()
23 {
24     scanf("%d%d",&n,&m);
25     for(int i=1;i<=m;++i)
26     {
27         scanf("%d%d%d",&u1,&v1,&w1);
28         add_edge(u1,v1,w1);
29         add_edge(v1,u1,w1);
30     }
31 }
32 void dfs(int k)
33 {
34     for(int l=head[k];l;l=edge[l].last)
35     {
36         if(!deepp[edge[l].v])
37         {
38             deepp[edge[l].v]=deepp[k]+1;
39             father[edge[l].v][0]=k;
40             dis[edge[l].v]=dis[k]+edge[l].w;
41             dfs(edge[l].v);
42         }
43     }
44 }
45 void pre_chuli()
46 {
47     for(int j=1;(1<<j)<=n;++j)
48       for(int i=1;i<=n;++i)
49          if(father[i][j-1]!=-1)
50          father[i][j]=father[father[i][j-1]][j-1];
51 }
52 int lca(int a,int b)
53 {
54     if(deepp[a]<deepp[b]) swap(a,b);
55     int i;
56     for(i=0;(1<<i)<=deepp[a];++i);
57     i--;
58     for(int j=i;j>=0;--j)
59       if(deepp[a]-deepp[b]>=(1<<j))
60       a=father[a][j];
61     if(a==b) return a;
62     for(int j=i;j>=0;--j)
63       if(father[a][j]!=-1&&father[a][j]!=father[b][j])
64       {
65             a=father[a][j];
66             b=father[b][j];
67       }
68     return father[a][0];
69 }
70 int main()
71 {
72     freopen("ThefallingofZLX.in","r",stdin);
73     freopen("ThefallingofZLX.out","w",stdout);
74     input();
75     memset(father,-1,sizeof(father));
76     dis[1]=0;
77     deepp[1]=1;
78     dfs(1);
79     pre_chuli();
80     scanf("%d",&Q);
81     for(int i=1;i<=Q;++i)
82     {
83         scanf("%d%d",&u1,&v1);
84         int ance=lca(u1,v1);
85         cout<<dis[u1]+dis[v1]-2*dis[ance]<<endl;
86     }
87     fclose(stdin);fclose(stdout);
88     return 0;
89 }

 

3.codevs 3287 貨車運輸

2013年NOIP全國聯賽提升組

 時間限制: 1 s
 空間限制: 128000 KB
 題目等級 : 鑽石 Diamond
 
題目描述  Description

A 國有 n 座城市,編號從 1 到 n,城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。如今有 q 輛貨車在運輸貨物,司機們想知道每輛車在不超過車輛限重的狀況下,最多能運多重的貨物。

輸入描述  Input Description

第一行有兩個用一個空格隔開的整數 n,m,表示 A 國有 n 座城市和 m 條道路。
接下來 m 行每行 3 個整數 x、y、z,每兩個整數之間用一個空格隔開,表示從 x 號城市到 y 號城市有一條限重爲 z 的道路。注意:x 不等於 y,兩座城市之間可能有多條道路。
接下來一行有一個整數 q,表示有 q 輛貨車須要運貨。
接下來 q 行,每行兩個整數 x、y,之間用一個空格隔開,表示一輛貨車須要從 x 城市運輸貨物到 y 城市,注意:x 不等於 y。

輸出描述  Output Description

輸出共有 q 行,每行一個整數,表示對於每一輛貨車,它的最大載重是多少。若是貨車不能到達目的地,輸出-1。

樣例輸入  Sample Input

4 3 
1 2 4 
2 3 3 
3 1 1 
3
1 3 
1 4 
1 3

樣例輸出  Sample Output

3
-1
3

數據範圍及提示  Data Size & Hint

對於 30%的數據,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000; 
對於 60%的數據,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000; 
對於 100%的數據,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。

/*--------------解析---------------------*/
拿到這道題,咱們求的是多組兩點間通路上最大的最小邊權,最大生成樹的兩點間最小邊權就知足這個性質,因此就用到到最大生成樹

很容易獲得60分算法

60分算法:(最大生成樹暴力)

       先找一個最大生成樹,而後把生成樹單獨放在一個相似生成樹存儲方式的三個數組裏,初始化並查集,而後從最大邊開始作kruskal,每次連邊時,都對問題集合進行掃描,當且僅當這兩個邊聯通且沒有更新過當前問題的答案,就用當前邊權更新。緣由很簡單,這是貪心,首先邊權時降序的,而後這個邊使兩點第一次聯通,那麼這個邊必定是通路上最小的邊。這就是60的算法。可是注意,題目並無說是否是一棵樹,因此它能夠是森林,可是因爲六十分算法原理對於森林這個性質並不依賴,因此,這個60算法不用考慮森林

那麼咱們想一下,這道題時間的瓶頸在哪,首先kruskal是o(m)的,這個不會爆時間,那麼剩下就是從最大生成樹上回答答案所用時間過長。

咱們想一下,若是是在logn時間內回答一個問題,並且是在樹上作的一個算法,尤爲是這個問題具備區間求和的性質,由於想想就知道這是顯然的

f[i→j]:=min(f[i→k],f[k→j]),

因此,咱們想到用樹上倍增算法解決。

100分算法(最大生成樹+樹上倍增):

首先清楚樹上倍增原理:f[i,j]:=f[f[I,j-1],j-1] f[I,j],表示 i向上2^j的節點

同時,設咱們要求的答案g[i,j],即從i向上走到達2^j層的節點所經過路徑上的最小值,轉移方程

g[I,j]:=min(g[f[I,j-1],j-1],g[I,j-1])

分析一下就是顯然的,g[f[I,j-1],j-1]表示的是i向上2^(j-1)的節點到2^j節點間的最小值,g[I,j-1] 是從i向上走到達2^(j-1)層的節點所經過路徑上的最小值,兩個區間合起來就是當前的區間長度,因此這個方程成立。對於初值,咱們能夠bfs求深度並記錄,與此同時,f[I,0]是父節點,g[I,0]是他向上走一層的邊的長度。而後按照倍增算法模板生成f g數組

求解時,用倍增算法模板便可,只不過有幾個細節

1、  別忘了給ans賦初值

2、  倍增算法永遠是倍增到最近公共祖先的下一層的兩個點,因此最後要比較一下ans與那兩個點的向上走一步的值

3、  記得這是個森林,要用一個數組記一下那個是父節點,我用的是並查集加bool數組,有個細節是不要直接把fa[i]當I的所在樹的根,要get一下,由於並查集的壓縮路徑是在每次get時才更新,當他fa變了,而他未被查詢過,就會致使fa過期了,要從新get一下

4、  在對齊兩個所求節點的層數時,別忘了更新答案

大概就是這些,還有相關代碼具體實現就不贅述了。

哦,對了,提交不要忘了刪除調試用的輸出

 

 1 /*
 2 60分代碼:暴力建q棵最大生成樹
 3 */
 4 #define N 10008 
 5 #define M 50007
 6 #include<iostream>
 7 using namespace std;
 8 #include<cstdio>
 9 #include<cstring>
10 #include<algorithm>
11 int n,m,q,fa[N],head[N],t=0;
12 struct Edge{
13     int u,v,w,last;
14     bool operator <(Edge P)
15     const{return w>P.w;}
16 }edge[M<<1];
17 int read()
18 {
19     int ret=0,ff=1;
20     char s=getchar();
21     while(s<'0'||s>'9')
22     {
23         if(s=='-') ff=-1;
24         s=getchar();
25     }
26     while(s>='0'&&s<='9')
27     {
28         ret=ret*10+s-'0';
29         s=getchar();
30     }
31     return ret;
32 }
33 inline void add_edge(int u,int v,int w)
34 {
35     ++t;
36     edge[t].u=u;
37     edge[t].v=v;
38     edge[t].w=w;
39     edge[t].last=head[u];
40     head[u]=t;
41 }
42 inline void inpu()
43 {
44     n=read();m=read();
45     int x,y,z;
46     for(int i=1;i<=m;++i)
47     {
48         x=read();y=read();z=read();
49         add_edge(x,y,z);
50     }
51 }
52 int find(int x)
53 {
54     return (fa[x]==x)?x:fa[x]=find(fa[x]);
55 }
56 int main()
57 {
58     freopen("truck.in","r",stdin);
59     freopen("truck.out","w",stdout);
60     inpu();
61     sort(edge+1,edge+t+1);
62     q=read();
63     int x,y;
64     for(int i=1;i<=q;++i)
65     {
66         x=read();y=read();
67         for(int i=1;i<=n;++i) fa[i]=i;
68         bool flag=false;
69         for(int i=1;i<=t;++i)
70         {
71             int x1=find(edge[i].u);
72             int y1=find(edge[i].v);
73             if(x1!=y1)
74             {
75                 fa[y1]=x1;
76             }
77             if(find(x)==find(y))
78             {
79                  printf("%d\n",edge[i].w);
80                  flag=true;
81                  break;
82             }
83         }
84         if(!flag) printf("-1\n");
85     }
86     fclose(stdin);
87     fclose(stdout);
88     return 0;
89 }

 

  1 #define N 10010
  2 #define inf 10000800
  3 #include<cstdio>
  4 #define M 50002
  5 #define D 18
  6 #include<cstring>
  7 #include<algorithm>
  8 #include<cstdio>
  9 using namespace std;
 10 int n,m,q,fa[N],t=0;
 11 struct EEdge{
 12     int u,v,w;
 13     bool operator <(EEdge P)
 14     const{return w>P.w;}
 15 }edge1[M];
 16 struct Edge{
 17     int v,w,last;
 18 }e[N];
 19 int dis[N][D+1],fath[N][D+1],dep[N],head[N];
 20 int read()
 21 {
 22     int ret=0,ff=1;
 23     char s=getchar();
 24     while(s<'0'||s>'9')
 25     {
 26         if(s=='-') ff=-1;
 27         s=getchar();
 28     }
 29     while(s>='0'&&s<='9')
 30     {
 31         ret=ret*10+s-'0';
 32         s=getchar();
 33     }
 34     return ret*ff;
 35 }
 36 void inpu()
 37 {
 38     n=read();m=read();
 39     for(int i=1;i<=m;++i)
 40     {
 41         edge1[i].u=read();
 42         edge1[i].v=read();
 43         edge1[i].w=read();
 44     }
 45 }
 46 int fi(int x)
 47 {
 48     return (fa[x]==x)?x:fa[x]=fi(fa[x]);
 49 }
 50 void add_edge(int u,int v,int w)
 51 {
 52     ++t;
 53     e[t].v=v;
 54     e[t].w=w;
 55     e[t].last=head[u];
 56     head[u]=t;
 57 }
 58 void ddkruskal()
 59 {
 60     for(int i=1;i<=n;++i) fa[i]=i;
 61     sort(edge1+1,edge1+m+1);
 62     int tsum=0;
 63     for(int i=1;i<=m;++i)
 64     {
 65         int x1=fi(edge1[i].u);
 66         int x2=fi(edge1[i].v);
 67         if(x1!=x2)
 68         {
 69             tsum++;
 70             fa[x2]=x1;
 71             add_edge(edge1[i].u,edge1[i].v,edge1[i].w);
 72             add_edge(edge1[i].v,edge1[i].u,edge1[i].w);
 73             if(tsum==n-1) break;
 74         }
 75     }
 76 }
 77 void dfs(int k)
 78 {
 79     for(int l=head[k];l;l=e[l].last)
 80     {
 81         if(!dep[e[l].v])
 82         {
 83             dep[e[l].v]=dep[k]+1;
 84             fath[e[l].v][0]=k;
 85             dis[e[l].v][0]=e[l].w;
 86             dfs(e[l].v);
 87         }
 88     }
 89 }
 90 void init()
 91 {
 92     for(int j=1;j<=D;++j)
 93       for(int i=1;i<=n;++i)
 94       {
 95           fath[i][j]=fath[fath[i][j-1]][j-1];
 96           dis[i][j]=min(dis[i][j-1],dis[fath[i][j-1]][j-1]);
 97       }
 98 }
 99 int lca(int a,int b)
100 {
101     int ans=inf;
102     if(dep[a]<dep[b]) swap(a,b);
103     for(int j=D;j>=0;--j) 
104     {
105         if(dep[a]-(1<<j)>=dep[b])
106         {
107             ans=min(ans,dis[a][j]);
108             a=fath[a][j];
109         }
110     }
111     if(a==b) return ans;
112     for(int j=D;j>=0;--j)
113     {
114         if(fath[a][j]!=fath[b][j])
115         {
116             ans=min(ans,min(dis[a][j],dis[b][j]));
117             a=fath[a][j];
118             b=fath[b][j];
119         }
120     }
121     ans=min(ans,min(dis[a][0],dis[b][0]));
122     return ans;
123 }
124 int main()
125 {
126     freopen("truck.in","r",stdin);
127     freopen("truck.out","w",stdout);
128     inpu();
129     ddkruskal();
130     q=read();
131     int x,y;
132     for(int i=1;i<=n;++i)
133     {
134         if(!dep[i])
135         {
136             dep[i]=1;
137             fath[i][0]=i;
138             dis[i][0]=inf;
139             dfs(i);
140         }
141     }
142     init();
143     for(int i=1;i<=q;++i)
144     {
145         x=read();y=read();
146         if(fi(x)!=fi(y))  printf("-1\n");
147         else printf("%d\n",lca(x,y));
148     }
149     fclose(stdin);
150     fclose(stdout);
151     return 0;
152 }
相關文章
相關標籤/搜索