以NOIP2013提高组day1 最后一道题为例来学的倍增和lca。其实这套题早就做过了,倍增和lca也学过,只不过当时没有理解清楚,所以今天再次学了一遍,虽然没有时间编程序了,但是先把思路和做法在这里梳理一遍,下次来编。
首先,倍增。(树上倍增)
f[i][j]表示在 j 节点向上跳2^ i 步后的节点。由2^ i =2^( i-1)+2^(i-1)可以得到递推式:f[i][j]=f[i-1][f[i-1][j]]。解释:从j节点向上跳2^ i 就相当于从j节点向上先跳2^(i-1)步到得节点,再从这个节点向上跳2^(i-1)步所到的点。
所以得到:
1 void bz()//倍增 2 { 3 for(int i=1;i<=14;i++)//i根据题意(深度)调大小 4 for(int j=1;j<=n;j++) 5 fa[i][j]=fa[i-1][fa[i-1][j]]; 6 }
然后就是找lca(最近公共祖先)
在这之前,首先要明确这两个点在树上的深度,先将较深的点向上跳到与另一点一样高(需要两点的深度差dh),然后再一起向上跳到最近公共祖先。怎么才能使两个点在同一深度呢?有两种方法,这里讲二进制的方法。深度差可以表示为二进制形式,如当dh=5时,为101,即这个点要在为1 的地方跳,先跳2^0步到一个节点,再从这个节点向上跳2^2步。可以用 if (1<< i & dh) 判断,当不等于0时,说明dh在第 i 位为1 ,需要跳,等于0时不需要跳就不管,每次更新跳到位置。
for(int i=0;i<=14;i++)//使两个点深度相同 { if(1<<i&dh)//位运算 { // t1=min(t1,minax[i][s]); s=fa[i][s]; } }
然后就是两个点一起向上跳到公共祖先,只需判断他们跳到的点是不是相同就可以了,因为如果超出了最近公共祖先那么他们跳到的节点就一定是同一节点,这时i 不可取,将 i 倒序循环,这样如果超过lca就会跳较少的步数到下面一点来判断是不是lca,如果这个时候又跳得太小了,那么更新现在跳到的节点,i 继续减小,从这个节点继续向上跳,重复上面的判断,由于跳的步数越来越少,越来越接近lca,那么最后一次一定是最接近lca的,这时,一定是跳两步多了,跳一步少了,所以刚好在lca下一个节点处,再加上1 即可。
1 int lca(int s,int v)//找最近公共祖先,并求出最小值 2 { 3 int t1=INF,t2=INF;//两边子树最小值 4 if(F(s)!=F(v))return -1;//判断是否连通 5 if(depth[v]>depth[s])//保证s在v下面 6 swap(s,v); 7 int dh=depth[s]-depth[v]; 8 for(int i=0;i<=14;i++)//使两个点深度相同 9 { 10 if(1<<i&dh)//位运算 11 { 12 t1=min(t1,minax[i][s]); 13 s=fa[i][s]; 14 } 15 16 } 17 if (s==v) return t1; //判断是否已经满足 18 for(int i=14;i>=0;i--) 19 { 20 if(fa[i][s]!=fa[i][v]) 21 { 22 t1=min(t1,minax[i][s]); 23 t2=min(t2,minax[i][v]); 24 s=fa[i][s]; 25 v=fa[i][v]; 26 } 27 } 28 //此时两点都在最近公共祖先的下面,只需再向上走一步 29 t1=min(t1,minax[0][s]); 30 t2=min(t2,minax[0][v]); 31 return min(t1,t2); 32 }
下来自己写一遍。。。谢谢anantheparty的博客。
2016 10 09
今天把货车运输这道题A了。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<queue> 6 #define maxn 10005 7 #define inf 100005 8 #define INF 12345678 9 using namespace std; 10 int n,m,q,fat[maxn],deth[maxn]; 11 int tot,he[maxn],ne[inf],to[inf],w[inf]; 12 int mimax[16][maxn],f[16][maxn]; 13 bool notin[inf],flag[maxn];//notin[inf] not notin[maxn] RE 14 struct pp{ 15 int x,y,v; 16 }; 17 pp road[inf]; 18 const int comp(const pp&a,const pp&b ) 19 { 20 return a.v>b.v; 21 } 22 void add(int a,int b,int c) 23 { 24 tot++;to[tot]=b;ne[tot]=he[a];w[tot]=c;he[a]=tot; 25 } 26 int find(int a) 27 { 28 if (fat[a]!=a) return fat[a]=find(fat[a]); 29 return fat[a]; 30 } 31 void kruskal() 32 { 33 for (int i=1;i<=n;i++) 34 fat[i]=i; 35 for (int i=1;i<=2*m;i++) 36 { 37 int r1=find(road[i].x),r2=find(road[i].y); 38 if (r1!=r2) fat[r2]=r1; 39 else notin[i]=true; 40 } 41 } 42 void dfs(int u) 43 { 44 for (int i=he[u];i;i=ne[i]) 45 if (!flag[to[i]]){ 46 flag[to[i]]=1; 47 deth[to[i]]=deth[u]+1; 48 f[0][to[i]]=u; 49 mimax[0][to[i]]=w[i];//mimax[0][to[i]] 50 dfs(to[i]); 51 } 52 } 53 void bz() 54 { 55 for (int i=1;i<=14;i++)// i=1!!! 56 for (int j=1;j<=n;j++) 57 { 58 f[i][j]=f[i-1][f[i-1][j]]; 59 mimax[i][j]=min(mimax[i-1][j],mimax[i-1][f[i-1][j]]);//mimax[i-1][j],f[i-1][j] 60 } 61 } 62 int lca(int a,int b) 63 { 64 int r1=INF,r2=INF; 65 if (find(a)!=find(b)) return -1; 66 if (deth[a]<deth[b]) swap(a,b); 67 int d=deth[a]-deth[b]; 68 for (int i=0;i<=14;i++) 69 { 70 if (1<<i & d) 71 { 72 r1=min(mimax[i][a],r1); 73 a=f[i][a]; 74 } 75 } 76 if (a==b) return r1; 77 for (int i=14;i>=0;i--) 78 { 79 if (f[i][a]!=f[i][b]) 80 { 81 r1=min(mimax[i][a],r1); 82 r2=min(mimax[i][b],r2); 83 a=f[i][a]; 84 b=f[i][b]; 85 } 86 } 87 r1=min(r1,mimax[0][a]);//! 88 r2=min(r2,mimax[0][b]);//! 还有一步 89 return min(r1,r2); 90 } 91 int main() 92 { 93 // freopen("truck_lca.in","r",stdin); 94 cin>>n>>m; 95 for (int i=1;i<=m;i++) 96 { 97 int a,b,c; 98 scanf("%d%d%d",&a,&b,&c); 99 road[i*2-1].x=a;road[i*2].x=b; 100 road[i*2-1].y=b;road[i*2].y=a; 101 road[i*2-1].v=c;road[i*2].v=c; 102 } 103 sort(road+1,road+1+2*m,comp); 104 kruskal(); 105 for (int i=1;i<=2*m;i++) 106 if (!notin[i]) { 107 add(road[i].x,road[i].y,road[i].v); 108 add(road[i].y,road[i].x,road[i].v); 109 } 110 for (int i=1;i<=n;i++) 111 if (!deth[i]){ 112 int r=find(i); 113 deth[r]=1; 114 flag[r]=1; 115 dfs(r); 116 } 117 bz(); 118 cin>>q; 119 for (int i=1;i<=q;i++) 120 { 121 int a,b; 122 scanf("%d%d",&a,&b); 123 printf("%d ",lca(a,b)); 124 } 125 return 0; 126 }
还是有很多小细节需要注意啊。。。