题目链接:
主要思路:
由于只有n个点,n-1条边,故可以转化为树。
先找到一个离他们最近的与他们距离相等的点,记为点P:
记他们之间的距离为len,若两点之间距离为奇数则答案为0,求出LCA从深度大的那个点开始向上跳(len/2)-1步到X,(根节点深度为0),即跳到P的一个儿子结点上。答案就是sz[p]-sz[X].若他们的深度相同,即他们的LCA就是P,那么答案要加上P的所有祖先(n-sz[p])在减去b能跳到的P的儿子结点的子树的节点个数.如果要查询的两个点在同一位置,则所有结点都满足条件,答案为n(在这里WA了三遍的我表示很重要)。
AC代码:
#include<cstdio>
#include<algorithm>
#define M 100005
using namespace std;
struct E{
int to,nx;
}edge[M<<1];
int tot,head[M];
void Addedge(int a,int b){
edge[++tot].to=b;
edge[tot].nx=head[a];
head[a]=tot;
}
int fa[M][20],dep[M],sz[M],son[M],top[M],L[M],R[M],T;
void dfs(int now){
L[now]=++T;
sz[now]=1;
son[now]=0;
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(nxt==fa[now][0])continue;
fa[nxt][0]=now;
dep[nxt]=dep[now]+1;
dfs(nxt);
if(sz[son[now]]<sz[nxt])son[now]=nxt;
sz[now]+=sz[nxt];
}
R[now]=T;
}
void dfs_top(int now){//初始化重儿子
if(son[now]){
top[son[now]]=top[now];
dfs_top(son[now]);
}
for(int i=head[now];i;i=edge[i].nx){
int nxt=edge[i].to;
if(nxt==son[now]||nxt==fa[now][0])continue;
top[nxt]=nxt;
dfs_top(nxt);
}
}
int LCA(int a,int b){//跳重链求LCA
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=fa[top[a]][0];
}
return dep[a]<dep[b]?a:b;
}
void Initfa(int n){
for(int j=1;j<20;j++){
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
}
void Up(int &x,int y){
for(int i=0;i<20;i++){
if(y&(1<<i))x=fa[x][i];
}
}
int n;
int solve(int a,int b,int x){
if(a==b)return n;//注意是返回n,不是1
if(dep[a]<dep[b])swap(a,b);//a为深度大的那个
int pre=a;
int cnt=dep[a]+dep[b]-2*dep[x];//算两点之间的距离
if(cnt%2==1)return 0;//奇数直接返回0
cnt/=2;
Up(a,cnt-1);
int ans=sz[fa[a][0]]-sz[a];
if(dep[pre]==dep[b]){//若x即为到a与b距离相等且最小的那个点(即P)
Up(b,cnt-1); //跳到P的儿子结点
return n-sz[x]+ans-sz[b];//还要加上他的所有祖先,减去b跳了之后的子树中结点的个数
}
return ans;
}
int main(){
dep[1]=1;
top[1]=1;
scanf("%d",&n);
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
Addedge(a,b);
Addedge(b,a);
}
dfs(1);
dfs_top(1);//这样求LCA会快一些
Initfa(n);
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
int x=LCA(a,b);
printf("%d\n",solve(a,b,x));
}
}