题解 CF519E 【A and B and Lecture Rooms】
经典树上LCA。
luogu原题阅读。
这道题简单地说就是给定树上的两个点x,y。求树上能使得到x,y距离相等的点数。
我们可以分类讨论。
-
若LCA到x和LCA到y的距离相等且x,y不是同一个点。
用总点数n-x这棵子树上的点数-y这棵子树上的点数=到x,y距离相等的点数。 -
若LCA到x和LCA到y的距离不相等,且分别为a,b。
(1)a+b为奇数,则a-b必定为奇数(和差)。
x,y的距离差必定同时加或减去一个偶数,永远不可能是0。
(2)a+b为偶数,不妨假设a>b,在a上必有一点距离LCA(a-b)/2到x,y距离相等。设这个点为P,以P为根的子树结点-P到x的结点数+1=到x,y距离相等的点数。 -
x,y是同一个点.
树上任何一个点都成立。
上代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
struct Edge{
int v,nxt;
}edge[maxn*2];
int head[maxn],tot;
void init() {
memset(head,-1,sizeof head);
tot = 0; //下一个等待被用的节点。
}
void addedge(int u,int v) { //u->v ,边权为w
edge[tot].v = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
int fa[maxn][20],dep[maxn],siz[maxn];
void dfs(int u,int pre) { //nlogn的预处理
fa[u][0] = pre;
siz[u] = 1;
for(int i=1;i<20;i++)
fa[u][i] = fa[fa[u][i-1]][i-1];
dep[u] = dep[pre] + 1;
for(int i=head[u];i!=-1;i=edge[i].nxt) {
int v = edge[i].v;
if(v==pre)
continue;
dfs(v,u);
siz[u] += siz[v];
}
}
int LCA(int u,int v) {
if(dep[u]>dep[v])
swap(u,v);
for(int i=19;i>=0;i--)
if(dep[fa[v][i]] >= dep[u]) v = fa[v][i];
if(u==v)
return u;
for(int i=19;i>=0;i--) {
if(fa[u][i]==fa[v][i])
continue;
u = fa[u][i];
v = fa[v][i];
}
return fa[u][0];
}
int jump_up(int u,int k) {
int i = 0;
while(k) {
if(k&1) u = fa[u][i];
k>>=1;
i++;
}
return u;
}
int n,m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
cin>>n;
for(int i=1;i<n;i++) {
int u,v;
cin>>u>>v;
addedge(u,v);
addedge(v,u);
}
cin>>m;
dfs(1,0); //nlogn
while(m--) { //mlogn
int a,b;
cin>>a>>b;
int lca = LCA(a,b);
if(a==b) {
cout<<n<<"\n";
}
else if(dep[a]==dep[b]) {
int x =jump_up(a,dep[a]-dep[lca]-1);
int y = jump_up(b,dep[b]-dep[lca]-1);
cout<< n - siz[x] -siz[y]<<"\n";
} else {
int d = dep[a]+dep[b] -2*dep[lca];
if(d&1) cout<<"0"<<"\n"; // if (d%2==1)
else {
if(dep[a]>dep[b]) swap(a,b);
int y = jump_up(b,d/2-1);
int x = fa[y][0];
cout<<siz[x] - siz[y]<<"\n";
}
}
}
return 0;
}