题意:
有一颗n个节点的树,有m个询问,每个询问给出x和y两个节点,让你求树上节点到这两个节点距离相等的数目。
思路:
①:如果x和y深度相同,则他们的最近公共祖先lca(x,y)的非这二个节点方向的节点以外的节点到x和y的距离相等.
②:如果x和深度不同,则他们的路径为奇数时无解,为偶数时中点在深度大的一方,该点到x和y的方向节点以外的节点到x和y的距离相等.
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read()
{
int x=0, g=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
{
g=-1;
}
c=getchar();
}
while(c<='9'&&c>='0')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*g;
}
int n, q, parent[22][100005], dep[100005], se[100005];
vector<int> g[100005];
int dfs(int v,int d, int f)
{
dep[v]=d;
parent[0][v]=f;
se[v]=1;
for(int i=0; i<g[v].size(); i++)
{
if(g[v][i]!=f)
se[v]+=dfs(g[v][i],d+1,v);
}
return se[v];
}
int lca(int u,int v)
{
if(dep[u]<dep[v])
{
swap(u,v);
}
for(int i=21;i>=0;i--)
{
if((dep[u]-dep[v])>=(1<<i))
{
u=parent[i][u];
}
}
if(u==v)
{
return u;
}
for(int i=21;i>=0;i--)
{
if(parent[i][u]!=parent[i][v])
{
u=parent[i][u];
v=parent[i][v];
}
}
return parent[0][u];
}
int shang(int v,int d)
{
for(int i=21;i>=0;i--)
{
if((d>>i)&1)
{
v=parent[i][v];
}
}
return v;
}
int fun(int x,int y)
{
if(dep[x]==dep[y])
{
if(x==y)
{
return n;
}
int v=lca(x,y);
int d=dep[x]-dep[v];
return n-se[shang(x,d-1)]-se[shang(y,d-1)];
}
else
{
int v=lca(x,y);
int dx=dep[x]-dep[v], dy=dep[y]-dep[v];
if(dx>dy)
{
int d=dx-dy;
if(d%2==0)
{
int ki=shang(x,dx-d/2-1);
return se[parent[0][ki]]-se[ki];
}
else
{
return 0;
}
}
else
{
int d=dy-dx;
if(d%2==0)
{
int ki=shang(y,dy-d/2-1);
return se[parent[0][ki]]-se[ki];
}
else
{
return 0;
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0; i<n-1; i++)
{
int u, v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
memset(parent,-1,sizeof(parent));
dfs(1,0,-1);
for(int i=1;i<21;i++)
{
for(int j=1;j<=n;j++)
{
if(parent[i-1][j]<1)
{
parent[i][j]=-1;
}
else
{
parent[i][j]=parent[i-1][parent[i-1][j]];
}
}
}
scanf("%d",&q);
while(q--)
{
int x, y;
scanf("%d%d",&x,&y);
cout << fun(x,y) << endl;
}
return 0;
}

京公网安备 11010502036488号