Codeforces 294 Div.2 E
题意是说, 给一棵树, 每次询问给出树上两点, 求到这两点距离相同的点的个数.
思路是先取定一个结点做根结点, 遍历求一次各个结点的父结点, 以该结点为根结点的子树中的结点数, 以及该结点的深度. 再利用递归预处理出每个结点向上2^j深度所对应的结点. 这步所得的结果事实上也可用于求LCA. 最后, 对于每次询问, 先考虑距离, 之后直接找出等距离所对应的结点, 再利用之前求出的结点数直接计算即可. 这种思路好像也是比较经典的. 详情见代码.
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=100005,maxm=25;
vector<int> E[maxn];
int n,rot;
int fat[maxm][maxn],dp[maxn],dn[maxn];
int dl[maxn],dln,sta;
int rai(int x,int d)
{
int t=0;
while(d>0)
{
if(d&1) x=fat[t][x];
d>>=1;++t;
}
return x;
}
int lca(int x,int y)
{
int d1=dp[x],d2=dp[y];
if(d1>d2) return lca(y,x);
if(d2>d1)
{
y=rai(y,d2-d1);
}
if(x==y) return x;
int ad=0,bd=d1;
while(bd-ad>1)
{
int xd=(bd+ad)/2;
if(rai(x,xd)==rai(y,xd)) bd=xd;
else ad=xd;
}
return rai(x,bd);
}
int dis(int x,int y)
{
int l=lca(x,y);
return dp[x]+dp[y]-2*dp[l];
}
int main()
{
int i,j;
scanf("%d",&n);
for(i=0;i<n-1;i++)
{
int x,y;scanf("%d %d",&x,&y);
--x;--y;
E[x].push_back(y);
E[y].push_back(x);
}
for(i=1,rot=0;i<n;i++) if(int(E[i].size())>int(E[rot].size())) rot=i;
memset(fat,-1,sizeof(fat));memset(dp,-1,sizeof(dp));memset(dn,0,sizeof(dn));
dp[rot]=0;dln=sta=0;dl[dln++]=rot;
while(sta<dln)
{
int x=dl[sta++];
for(i=0;i<E[x].size();i++)
{
int y=E[x][i];
if(dp[y]<0)
{
fat[0][y]=x;
dl[dln++]=y;
dp[y]=dp[x]+1;
}
}
}
for(i=n-1;i>=0;i--)
{
int x=dl[i];
dn[x]=1;
for(j=0;j<E[x].size();j++)
{
int y=E[x][j];
if(y!=fat[0][x]) dn[x]+=dn[y];
}
}
for(i=0;i<n;i++)
{
int x=dl[i];
for(j=1;(1<<j)<=dp[x];j++)
{
fat[j][x]=fat[j-1][fat[j-1][x]];
}
}
int Q;
scanf("%d",&Q);
while(Q--)
{
int x,y;scanf("%d %d",&x,&y);
--x;--y;
int dt=dis(x,y);
if(dt%2) {printf("0\n");continue;}
if(x==y) {printf("%d\n",n);continue;}
dt>>=1;
if(dp[x]==dp[y])
{
int lx=rai(x,dt-1),ly=rai(y,dt-1);
printf("%d\n",n-dn[lx]-dn[ly]);
}
else if(dp[x]<dp[y])
{
int lt=rai(y,dt),ly=rai(y,dt-1);
printf("%d\n",dn[lt]-dn[ly]);
}
else
{
int lt=rai(x,dt),lx=rai(x,dt-1);
printf("%d\n",dn[lt]-dn[lx]);
}
}
return 0;
}