CF519E A and B and Lecture Rooms
这道题目...真是一道好题...题出的好!难度适中!覆盖知识点广!题目有着切合实际的背景!解法比较自然!给出题人点赞 !
题意:
给你一棵树,每次询问给出两个点,问树上有多少点到这两个点的距离相等。
$Sol$
树上距离...嗯肯定有$LCA$,但是看起来还是毫无思路的样子啊...怎么办。
这个时候分类讨论一定是个不错的选择(逃)
由于我只会树上倍增,而且大部分时候它还是很好用的,还能求出许多附属需要的东西,所以我们先用树上倍增法求出树上这两点的距离,进行分类讨论。(设置一个a的深度大于b的深度的前提)
1)当距离为奇数:无解,答案为0.
比如答案要求到a和b距离相等的点,ab距离为奇数,显然没有。
2)当距离为偶数
· a,b在同一条链上。
显然b是a,b的LCA,答案就是a.b节点的中点它所有子树的大小除去有a的那棵子树大小。即$size[中点]-size[中点的a所在子树]$
Explanation:
(开始在这里理解有偏差,ab中点有可能不止a一个子树)。在ab中点的子树上的点到ab中点的过程中,距离是一定的。而ab中点到a,b距离又分别相等,所以距离相等。
· a,b分别在他们的LCA两侧,且二者的深度相同。
显然LCA是一个合法的点,而且LCA的祖先们也是合法的点,而且LCA的除a,b所在的所有子树上的所有点也是合法的。(道理同上),答案就是$n-size[lca的a所在子树]-size[lca的b所在子树]$
· a,b分别在他们的LCA两侧,且二者深度不相同。
有了前面的基础,这里就会好想一点,答案正是$size[mid]-size[mid的子树中有a的那棵]$
从上面的分析我们也可以看出,我们要求的,也只不过是size,d(深度)。这些在进行倍增预处理时可以顺带求出。然后求“有a的那棵子树”怎么求?我们其实暴力向上倍增一下即可(不是暴力向上跳,是倍增,从大到小缩小范围)。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 #include<cmath> 5 #define maxn 100090 6 7 using namespace std; 8 9 int n,m,tot,t; 10 int head[maxn],size[maxn],d[maxn]; 11 int f[maxn][30]; 12 bool vis[maxn]; 13 struct node{ 14 int to,next; 15 }edge[maxn*2]; 16 17 void add(int x,int y) 18 { 19 edge[++tot].to=y; 20 edge[tot].next=head[x]; 21 head[x]=tot; 22 } 23 24 void init_1() 25 { 26 queue<int>q; 27 q.push(1);d[1]=1; 28 while(!q.empty()) 29 { 30 int u=q.front();q.pop(); 31 for(int i=head[u];i;i=edge[i].next) 32 { 33 int v=edge[i].to; 34 if(d[v]) continue; 35 d[v]=d[u]+1; 36 f[v][0]=u; 37 for(int j=1;j<=t;j++) 38 f[v][j]=f[f[v][j-1]][j-1]; 39 q.push(v); 40 } 41 } 42 } 43 44 void init_2(int u) 45 { 46 vis[u]=1;size[u]++; 47 for(int i=head[u];i;i=edge[i].next) 48 { 49 int v=edge[i].to; 50 if(vis[v]) continue; 51 init_2(v); 52 size[u]+=size[v]; 53 } 54 } 55 56 int find(int x,int dep) 57 { 58 int hu=x; 59 for(int i=t;i>=0;i--) 60 { 61 if(d[hu]-(1<<i)<dep) continue; 62 hu=f[hu][i]; 63 } 64 return hu; 65 } 66 67 int ask(int x,int y) 68 { 69 if(d[x]<d[y]) swap(x,y); 70 int prex=x,prey=y,lca=0,tmp=0; 71 for(int i=t;i>=0;i--) 72 if(d[f[x][i]]>=d[y]) x=f[x][i]; 73 if(x==y) lca=x; 74 else 75 { 76 for(int i=t;i>=0;i--) 77 if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 78 lca=f[x][0]; 79 } 80 x=prex,tmp=y,y=prey; 81 int dis=d[x]+d[y]-2*d[lca]; 82 if(dis&1) return 0; 83 if(lca==y) 84 { 85 int mid=(d[x]+d[y])>>1; 86 return size[find(x,mid)]-size[find(x,mid+1)]; 87 } 88 else 89 { 90 if(d[x]==d[y]) 91 return n-size[find(x,d[lca]+1)]-size[find(y,d[lca]+1)]; 92 else 93 { 94 int mid=d[x]-(dis>>1); 95 return size[find(x,mid)]-size[find(x,mid+1)]; 96 } 97 } 98 } 99 100 int main() 101 { 102 scanf("%d",&n); 103 t=log2(n)+1; 104 for(int i=1;i<=n-1;i++) 105 { 106 int x=0,y=0; 107 scanf("%d%d",&x,&y); 108 add(x,y);add(y,x); 109 } 110 init_1(); 111 init_2(1); 112 scanf("%d",&m); 113 for(int i=1;i<=m;i++) 114 { 115 int x=0,y=0; 116 scanf("%d%d",&x,&y); 117 if(x==y){printf("%d\n",n);continue;} 118 printf("%d\n",ask(x,y)); 119 } 120 return 0; 121 }
参考文献:https://www.cnblogs.com/qixingzhi/p/9302038.html(从这里学会的)