493 字
2 分钟
约会
题目描述
输入输出
样例输入
4
1 2
1 3
2 4
1
2 3
样例输出
1
提示
题解
先建出树来,顺便求出每个节点所在子树的size
然后对于每一个询问我们先跑LCA
然后通过树上倍增求出他们的中点
如果他是LCA那么ans = n - size(有起点的儿子) - size(有终点的儿子)
否则 ans = size(mid) - size(有起点的儿子) - size(有终点的儿子)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct edge
{
int END, next;
} v[200005];
int first[100005], p, n;
void add(int a, int b)
{
v[p].END = b;
v[p].next = first[a];
first[a] = p++;
}
int size[100005];
int f[100005][30];
int dep[100005];
void dfs(int rt, int fa, int dp)
{
dep[rt] = dp;
f[rt][0] = fa;
for (int i = 1; i <= 20; i++)
f[rt][i] = f[f[rt][i - 1]][i - 1];
size[rt] = 1;
for (int i = first[rt]; i != -1; i = v[i].next)
if (v[i].END != fa)
{
dfs(v[i].END, rt, dp + 1);
size[rt] += size[v[i].END];
}
}
int LCA(int a, int b)
{
if (dep[a] < dep[b])
swap(a, b);
int t = dep[a] - dep[b];
for (int i = 20; i >= 0; i--)
if (t & (1 << i))
a = f[a][i];
if (a == b)
return a;
for (int i = 20; i >= 0; i--)
if (f[a][i] != f[b][i])
a = f[a][i], b = f[b][i];
return f[a][0];
}
int Query(int a, int b)
{
if (a == b)
return n;
if (dep[a] < dep[b])
swap(a, b);
int Lca = LCA(a, b);
int l = dep[a] - dep[Lca];
int r = dep[b] - dep[Lca];
if ((l + r) & 1)
return 0;
int mid = l + r >> 1;
int k = a;
for (int i = 20; i >= 0; i--)
if (mid & (1 << i))
k = f[k][i];
if (k == Lca)
{
int t = dep[a] - dep[Lca] - 1;
for (int i = 20; i >= 0; i--)
if (t & (1 << i))
a = f[a][i], b = f[b][i];
return n - size[a] - size[b];
}
else
{
int t = dep[a] - dep[k] - 1;
for (int i = 20; i >= 0; i--)
if (t & (1 << i))
a = f[a][i];
return size[k] - size[a];
}
}
int main(int argc, char const *argv[])
{
memset(first,-1,sizeof(first));
int a, b;
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs(1, 0, 0);
int m;
scanf("%d",&m);
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &a, &b);
printf("%d\n", Query(a, b));
}
return 0;
}