LCA模块+求树上两点距离最短
1、[POJ 1330]Nearest Common Ancestors
LCA的模版题
因为没有告诉我们谁是根节点但是告诉我们方向了,所以我们在每次建边的时候可以记录一下,节点的父亲,如果一个节点没有父亲,那么他就是根节点。
在倍增找LCA的时候我们首先要知道每个点的深度,bfs就好了dep数组记录深度(这道题我用的dis),一边bfs一边找每个节点的2的j次方的父亲,正确性的话,一个10进制数可以转化为一个二进制数,所以一个数就可以分解为$2^{(1.2.3.4.....)}$次方之和,所以我们才可以倍增,且一定会找到LCA。
dfs之后开始找LCA,我们每次希望从一个深度较大的点往上跳,但是我们又很懒,不想多判断情况,所以我们人为使x节点的深度<y节点的深度。每次往上跳$2^j$次(从大到小,因为可能先跳小的再跳大的会直接被跳过),直到跳到LCA的下一个节点,然后返回他的父亲,对LCA理解的并不是那么深刻,所以可能有很多地方讲的会有错误和遗漏。
代码,缩进太奇怪了,写完博客回小屋粘:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <queue> 5 #include <cmath> 6 #include <cstring> 7 using namespace std; 8 int t,n,x,y,root; 9 queue<int> q; 10 const int maxn=1e5; 11 struct node{ 12 int to,next; 13 }ed[maxn]; 14 int cnt,dis[maxn],f[maxn][20],fa[maxn]; 15 int head[maxn],tot; 16 void add(int u,int to){ 17 ed[++tot].to=to; 18 ed[tot].next=head[u]; 19 head[u]=tot; 20 } 21 void bfs(int s){ 22 q.push(s); 23 dis[s]=1; 24 while (!q.empty()){ 25 int x=q.front();q.pop(); 26 for (int i = head[x];i;i=ed[i].next){ 27 int to=ed[i].to; 28 if (dis[to]) continue; 29 dis[to]=dis[x]+1; 30 f[to][0]=x; 31 for (int j = 1;j <= cnt;j++) f[to][j]=f[f[to][j-1]][j-1]; 32 q.push(to); 33 } 34 } 35 } 36 int lca(int x,int y){ 37 if (dis[x]>dis[y]) swap(x,y); 38 for (int i = cnt;i>=0;i--) 39 if (dis[f[y][i]]>=dis[x]) 40 y=f[y][i]; 41 if (x==y) return x; 42 for (int i =cnt;i >= 0;i--) 43 if (f[x][i]!=f[y][i]) 44 x=f[x][i],y=f[y][i]; 45 return f[x][0]; 46 } 47 int main(){ 48 scanf ("%d",&t); 49 while (t--){ 50 memset(head,0,sizeof(head)); 51 memset(fa,0,sizeof(fa)); 52 memset(f,0,sizeof(f)); 53 memset(dis,0,sizeof(dis)); 54 tot=0; 55 scanf ("%d",&n); 56 cnt=log(n)/log(2)+1; 57 for (int i = 1;i <= n-1;i++){ 58 scanf ("%d%d",&x,&y); 59 add(x,y);fa[y]=x; 60 } 61 for (int i = 1;i <= n;i++) if (!fa[i]){root=i;break;} 62 63 bfs(root); 64 scanf ("%d%d",&x,&y); 65 int ans=lca(x,y); 66 cout<<ans<<endl; 67 } 68 }
2、[HDU 2586]How far away
LCA求树上两点的距离模版,和LCA模版没什么区别,在bfs的时候多加个数组记录一下根节点到当前节点的距离就好了,然后应用容斥的思想,不会画图就直接给结论了,a$a$-$b$的距离=根节点-$a$的距离+根节点-$b$的距离-2*(根节点-LCA(a,b))的距离,应该挺显然的。另外因为没有给出跟节点,也没给出边的方向,但其实我们在bfs的时候同时也遍历一次dfs序,选取哪个节点,按照什么顺序其实都对答案没什么影响,就随便来就好了。
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #include <queue> 7 using namespace std; 8 int t,n,m,x,y,w,cnt; 9 const int maxn=1e5; 10 int head[maxn],tot,dep[maxn],dis[maxn],f[maxn][30]; 11 struct node{ 12 int to,next,w; 13 }ed[maxn]; 14 void add(int u,int to,int w){ 15 ed[++tot].to=to; 16 ed[tot].w=w; 17 ed[tot].next=head[u]; 18 head[u]=tot; 19 } 20 queue<int> q; 21 void dfs(int s){ 22 q.push(s); dep[s]=1; 23 while (!q.empty()){ 24 int x=q.front();q.pop(); 25 for(int i = head[x];i;i=ed[i].next){ 26 int to=ed[i].to; 27 if (dep[to]) continue; 28 dep[to]=dep[x]+1; 29 dis[to]=dis[x]+ed[i].w; 30 f[to][0]=x; 31 for (int j = 1;j <= cnt;j++) f[to][j]=f[f[to][j-1]][j-1]; 32 q.push(to); 33 } 34 } 35 } 36 int lca(int x,int y){ 37 if (dep[x]>dep[y]) swap(x,y); 38 for (int i = cnt;i >= 0;i--) 39 if (dep[f[y][i]]>=dep[x]) y=f[y][i]; 40 if (x==y) return x; 41 for (int i = cnt;i >= 0;i--) 42 if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 43 return f[x][0]; 44 } 45 int main(){ 46 scanf ("%d",&t); 47 while (t--){ 48 memset(head,0,sizeof(head)); 49 memset(dis,0,sizeof(dis)); 50 memset(dep,0,sizeof(dep)); 51 memset(f,0,sizeof(f)); 52 tot=0; 53 scanf ("%d%d",&n,&m); 54 for (int i = 1;i <= n-1;i++){ 55 scanf ("%d%d%d",&x,&y,&w); 56 add(x,y,w);add(y,x,w); 57 } 58 cnt=log(n)/log(2)+1; 59 dis[1]=0;dfs(1); 60 for (int i = 1;i <= m;i++){ 61 scanf ("%d%d",&x,&y); 62 int ans=dis[x]+dis[y]-2*dis[lca(x,y)]; 63 cout<<ans<<endl; 64 } 65 } 66 return 0; 67 }
3、[BZOJ 1787][AHOI 2008]紧急集合
前两道题的变式,我们希望路径最短,就希望路径不重复(不知道对不对),然后,然后就求完了
代码:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <queue> 5 #include <cmath> 6 #include <cstring> 7 using namespace std; 8 int read() 9 { 10 int a = 0,x = 1; 11 char ch = getchar(); 12 while(ch > '9' || ch < '0') { 13 if(ch == '-') x = -1; 14 ch = getchar(); 15 } 16 while(ch >= '0' && ch <= '9') { 17 a = a*10 + ch-'0'; 18 ch = getchar(); 19 } 20 return a*x; 21 } 22 int n,m,a,b,x,y,z; 23 const int maxn=1e6+10; 24 int head[maxn],dep[maxn],f[maxn][32],tot,cnt; 25 struct node{ 26 int to,next; 27 }ed[maxn]; 28 inline void add(int u,int to){ 29 ed[++tot].to=to; 30 ed[tot].next=head[u]; 31 head[u]=tot; 32 } 33 queue<int> q; 34 inline void bfs(int s){ 35 q.push(s);dep[s]=1; 36 while (!q.empty()){ 37 int x=q.front();q.pop(); 38 for (register int i = head[x];i;i=ed[i].next){ 39 int to=ed[i].to; 40 if (dep[to]) continue; 41 dep[to]=dep[x]+1; 42 f[to][0]=x; 43 for (register int j = 1;j <= cnt;j++) f[to][j]=f[f[to][j-1]][j-1]; 44 q.push(to); 45 } 46 } 47 } 48 inline int lca(int x,int y){ 49 if (dep[x]>dep[y]) swap(x,y); 50 for (int i = cnt;i >= 0;i--) 51 if ( dep[f[y][i]]>=dep[x]) y=f[y][i]; 52 if (x==y) return x; 53 for (register int i = cnt;i >= 0;i--) 54 if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 55 return f[x][0]; 56 } 57 int main(){ 58 n = read(),m = read(); 59 for (register int i = 1;i <= n-1;i++){ 60 a = read(),b = read(); 61 add(a,b);add(b,a); 62 } 63 cnt=30; 64 bfs(1); 65 for (register int i = 1;i <= m;i++){ 66 int t; 67 x = read(),y = read(),z = read(); 68 int t1=lca(x,y); 69 int t2=lca(x,z); 70 int t3=lca(y,z); 71 if (t1==t2) t=t3; 72 if (t2==t3) t=t1; 73 if (t1==t3) t=t2; 74 int ans=dep[x]+dep[y]+dep[z]-dep[t1]-dep[t2]-dep[t3]; 75 printf("%d %d\n",t,ans); 76 } 77 return 0; 78 }