【问题描述】
现有一棵 n 个节点的棵, 树上每条边的长度均为 1。 给出 m 个询问, 每次询问两个节 点 x,y, 求树上到 x,y 两个点距离相同的节点数量。
【输入格式】
第一个整数 n, 表示树有 n 个点。
接下来 n-1 行每行两整数 a, b, 表示从 a 到 b 有一条边。
接下来一行一个整数 m, 表示有 m 个询问。
接下来 m 行每行两整数 x, y, 询问到 x 和 y 距离相同的点的数量。
【输出格式】
共 m 行, 每行一个整数表示询问的答案。
【输入样例】
7
1 2
1 3
2 4
2 5
3 6
3 7
3
1 2
4 5
2 3
【输出样例】
0
5
1
【数据范围】
对于 30%的数据, 满足 n≤50, m≤50
对于 60%的数据, 满足 n≤1000, m≤1000
对于 100%的数据, 满足 n≤100000, m≤100000
【来源】
noip2016四校联考,巴蜀命题
#include<cctype>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#define maxn 100005
using namespace std;
vector<int>g[maxn];
int n,m,fa[maxn],depth[maxn],dist[maxn],sz[maxn],depthnum[maxn];
int marka,markb;
//depthnum[i]=深度为i的节点数量
//dist[i]=所有深度小于i的节点的数量
//sz[i]=以节点i为根的子树的节点总数
void read(int &x)
{
x=0;
char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
}
void build_tree(int i)
{
for(int j=0;j<g[i].size();j++)
{
int k=g[i][j];
if(fa[k])continue;
fa[k]=i;depth[k]=depth[i]+1;
build_tree(k);
sz[i]+=sz[k];
}
sz[i]++;//i节点包括在以i为根的子树里
}
int LCA(int a,int b)
{
while(depth[a]>depth[b]) a=fa[a];
while(a!=b) marka=a,a=fa[a],markb=b,b=fa[b];
return a;
}
void solve()
{
read(m);
while(m--)
{
int a,b;
read(a);read(b);
if(depth[b]>depth[a]) swap(a,b);
if(a==b) printf("%d\n",n);
else
{
int root=LCA(a,b),da=depth[root]-depth[a],db=depth[root]-depth[b];
if((da+db)%2) printf("0\n");
else if(depth[a]==depth[b]) printf("%d\n",n-sz[marka]-sz[markb]);
else
{
int d=da+db;//a与b的距离
int mid=depth[a]+d/2,now=a,last=a;
while(depth[now]!=mid) last=now,now=fa[now];
printf("%d\n",sz[now]-sz[last]);
}
}
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("Tree.out","w",stdout);
read(n);
for(int i=1;i<n;i++)
{
int x,y;
read(x);read(y);
g[x].push_back(y);
g[y].push_back(x);
}
fa[1]=1;
build_tree(1);
solve();
return 0;
}