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 }

 

posted @ 2020-09-21 12:56  小又又  阅读(409)  评论(0编辑  收藏  举报