题目描述
JYY 有两棵树 A A A 和 B B B:树 A A A 有 N N N 个点,编号为 1 1 1 到 N N N;树 B B B 有 N + 1 N+1 N+1 个节点,编号为 1 1 1 到 N + 1 N+1 N+1。JYY 知道树 B B B 恰好是由树 A A A 加上一个叶节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是树 B B B 中的哪一个叶节点呢?
输入输出格式
输入格式:
输入一行包含一个正整数
N
N
N。接下来
N
−
1
N-1
N−1 行,描述树
A
A
A,每行包含两个整数表示树
A
A
A 中的一条边; 接下来
N
N
N 行,描述树
B
B
B,每行包含两个整数表示树
B
B
B 中的一条边。
输出格式:
输出一行一个整数,表示树
B
B
B 中相比树
A
A
A 多余的那个叶子的编号。如果有多个符合要求的叶子,输出
B
B
B 中编号最小的那一个的编号。
输入输出样例
输入样例#1:
5
1 2
2 3
1 4
1 5
1 2
2 3
3 4
4 5
3 6
输出样例#1:
1
说明
对于所有数据,
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105
分析:
显然是树hash的题目。
对于树
A
A
A,我们要求出每一个点为根的hash值。树
B
B
B要求出每一个叶子为根的hash值。
可以使用二次dfs的方式求得。设
f
[
i
]
f[i]
f[i]为当前节点的hash值,
g
[
i
]
g[i]
g[i]为以
i
i
i为根的子树hash值。
考虑一个点的hash值为所有儿子的
g
[
j
]
∗
k
e
y
+
s
i
z
e
[
j
]
g[j]*key+size[j]
g[j]∗key+size[j]的异或和,其中
k
e
y
key
key为位值,这里不用加起来取模的方式,采用异或可以更好算
f
f
f。只要可以通过再异或一个儿子达到去掉的结果。
对于B来说,去掉某个叶子节点的hash值等于他父亲的hash值异或上这个去掉叶子的hash值。所以我们要选一个不是叶子的节点做根。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#define LL long long
const int maxn=1e5+7;
const int key=10189488;
const LL mod=1e9+7;
using namespace std;
int n,x,y;
int deg[maxn];
map <int,int> h;
struct tree{
int n,cnt;
int size[maxn],ls[maxn];
int f[maxn],g[maxn],fa[maxn];
struct edge{
int y,next;
}e[maxn*2];
void add(int x,int y)
{
e[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
int get(int x,int y)
{
return ((LL)x*(LL)key+y)%mod;
}
void dfs1(int x,int F)
{
g[x]=size[x]=1;
fa[x]=F;
for (int i=ls[x];i>0;i=e[i].next)
{
int y=e[i].y;
if (y==fa[x]) continue;
dfs1(y,x);
size[x]+=size[y];
g[x]^=get(g[y],size[y]);
}
}
void dfs2(int x)
{
if (!fa[x]) f[x]=g[x];
else f[x]=g[x]^get(f[fa[x]]^get(g[x],size[x]),n-size[x]);
for (int i=ls[x];i>0;i=e[i].next)
{
int y=e[i].y;
if (y==fa[x]) continue;
dfs2(y);
}
}
int check(int x)
{
return f[fa[x]]^get(g[x],1);
}
}A,B;
bool isleaf(int x)
{
return deg[x]==1;
}
int main()
{
// freopen("data.in","r",stdin);
scanf("%d",&A.n);
for (int i=1;i<A.n;i++)
{
scanf("%d%d",&x,&y);
A.add(x,y);
A.add(y,x);
}
A.dfs1(1,0),A.dfs2(1);
for (int i=1;i<=A.n;i++) h[A.f[i]]=1;
B.n=A.n+1;
for (int i=1;i<B.n;i++)
{
scanf("%d%d",&x,&y);
B.add(x,y);
B.add(y,x);
deg[x]++,deg[y]++;
}
for (int i=1;i<=B.n;i++)
{
if (!isleaf(i))
{
B.dfs1(i,0);
B.dfs2(i);
break;
}
}
for (int i=1;i<=B.n;i++)
{
if (isleaf(i))
{
int k=B.check(i);
if (h[k])
{
printf("%d",i);
break;
}
}
}
}