题意
有一棵有n个节点的树,每次询问给出两个点x,y,求有多少个点z满足dis(x,z)=dis(y,z)
分析
感觉是分类讨论。一开始想的情况有很多种,而且很复杂。
好在没有直接开始写orz
仔细讨论发现主要需要考虑的只有两种情况。
那么接下来是分类讨论。
1.x,y之间的距离是奇数,那么无解,输出0.
2.如果x==y,那么输出n.
然后是两类需要思考的情况。
首先记离x,y距离相等,且到两点距离最短的点为pos。
那么pos存在的位置可能是
1
pos=lca
对于这种情况,答案等于n-x所在子树点的个数-y所在子树点的个数
2
pos!=lca
因为在lca到x或者lca到y上的情况是一样的,所以记较深的点为x。
那么显然pos的子树除了包含k的子树之外,点的个数就是贡献。
结束。
实现起来稍微有点麻烦…树链剖分万岁 L(‘ω’)┘三└(‘ω’)」
code
#include<bits/stdc++.h>
using namespace std;
void read(int &x){
x=0; char c=getchar();
for (;c<48;c=getchar());
for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
#define M 100005
struct ed{
int x,nx;
}e[M<<1];
int nx[M],ecnt,cnt[M],de[M],fa[M],top[M],son[M],tot,dfn[M],id[M];
void add(int x,int y){
e[ecnt]=(ed){y,nx[x]};
nx[x]=ecnt++;
}
void dfs1(int f,int x){
fa[x]=f; cnt[x]=1;;
if (x!=1)de[x]=de[f]+1;
son[x]=0;
for (int i=nx[x];~i;i=e[i].nx)if (e[i].x!=f){
dfs1(x,e[i].x);
cnt[x]+=cnt[e[i].x];
if (cnt[e[i].x]>cnt[son[x]])son[x]=e[i].x;
}
}
void dfs2(int f,int x){
top[x]=f; dfn[x]=++tot; id[tot]=x;
if (son[x])dfs2(f,son[x]);
for (int i=nx[x];~i;i=e[i].nx)if (e[i].x!=son[x]&&e[i].x!=fa[x]){
dfs2(e[i].x,e[i].x);
}
}
int lca(int x,int y){
for (;top[x]!=top[y];){
if (de[top[x]]>de[top[y]])x=fa[top[x]];
else y=fa[top[y]];
}
if (de[x]<de[y])return x; return y;
}
int get(int x,int dep){//x到根的链上,深度为dep的点
for (;de[top[x]]>dep;){
x=fa[top[x]];
}
return id[dfn[x]-(de[x]-dep)];
}
void qu(int x,int y){
if (x==y){
printf("%d\n",cnt[1]);
return ;
}
int Lca=lca(x,y);
if ((de[x]+de[y])%2){
printf("0\n");
return ;
}
if (de[x]==de[y]){
printf("%d\n",cnt[1]-cnt[get(x,de[Lca]+1)]-cnt[get(y,de[Lca]+1)]);
return ;
}
else {
int pos;
if (de[x]<de[y])swap(x,y);
pos=get(x,de[x]-(de[x]+de[y]-2*de[Lca])/2);
printf("%d\n",cnt[pos]-cnt[get(x,de[pos]+1)]);
}
}
int main(){
// freopen("1.in","r",stdin);
int n,x,y,q;
memset(nx,-1,sizeof(nx));
read(n);
for (int i=1;i<n;i++){
read(x); read(y);
add(x,y); add(y,x);
}
dfs1(1,1);
dfs2(1,1);
read(q);
//printf("done\n");
for (int i=1;i<=q;i++){
read(x); read(y);
qu(x,y);
}
return 0;
}