【描述】
JYY 有两棵树 A 和 B :树 A 有 N 个点,编号为 1 到 N ;树 B 有 N+1 个点,编号为 1 到 N+1 。
JYY 知道树 B 恰好是由树 A 加上一个叶节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是树 B 中的哪一个叶节点呢?
【输入】
输入一行包含一个正整数 N。 接下来 N−1 行,描述树 A ,每行包含两个整数表示树 A 中的一条边; 接下来 N 行,描述树 B,每行包含两个整数表示树 B 中的一条边。
【输出】
输出一行一个整数,表示树 B 中相比树 A 多余的那个叶子的编号。如果有多个符合要求的叶子,输出 B 中编号最小的那一个的编号。
【样例输入】
5
1 2
2 3
1 4
1 5
1 2
2 3
3 4
4 5
3 6
【样例输出】
1
【提示】
对于所有数据,1≤N≤10^5
【思路】
这道题也是一道好题。首先你需要学会树hash,其实这个知识点也不难,就是给予每个点一个值,这个值与子树形态有关,用于判断两颗树是否相同。比如这道题,我的hash函数就是:
显然,树形态如果相同,其根节点的hash值一定相同。所以我们对于这道题就有了一个暴力的思路,去掉每一个叶子,判断是否同构。但这样是
。因为我们需要知道A树以每一个点为根的hash值,这是
。这就是为什么这道题的hash函数最好与异或有关。因为这样进行换根dp时更方便。我们只需要去掉自己对父亲的影响,然后父亲此时的hash值就是当前点为根是父亲的hash值。这样我们对两棵树就都可以进行一次换根dp。然后再枚举叶子,去掉它对与之相连的点的影响,判断与之相连的点此时的hash在A树中是否出现过即可。注意,最好不要选择B树的叶子节点为根dp,否则去掉其对与之相连的点的影响的操作会比较不便。时间复杂度
。
代码:
#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define re register
using namespace std;
const int N=2e5+5;
const int seed=5200827;
tr1::unordered_map<long long,bool>vis;
inline int red(){
int data=0;int w=1; char ch=0;
ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
int n,m,a,b,c,siz[N];
long long f[N],dp[N];
vector<int>g[N];
long long hash;
inline void add(const int&a,const int&b){g[a].push_back(b);g[b].push_back(a);}
void dfs1(int u,int fa){siz[u]=1;
for(int re i=g[u].size()-1;~i;--i){
int v=g[u][i];
if(v==fa)continue;
dfs1(v,u);siz[u]+=siz[v];
f[u]^=(f[v]*seed+1ll*siz[v]*siz[v]);
}
}void dfs2(int u,int fa){
for(int re i=g[u].size()-1;~i;--i){
int v=g[u][i];
if(v==fa)continue;
dp[v]=f[v]^((dp[u]^(f[v]*seed+1ll*siz[v]*siz[v]))*seed+1ll*(n+(u>n)-siz[v])*(n+(u>n)-siz[v]));
dfs2(v,u);
}
}
int main(){
n=red();int s;
if(n==1)return puts("1");
for(int re i=1;i^n;++i)add(red(),red());
for(int re i=0;i^n;++i)add(red()+n,red()+n);
for(int re i=n+1;i<((n+1)<<1);++i)
if(g[i].size()>1){s=i;break;}
dfs1(1,0);dfs1(s,0);
dp[1]=f[1];dp[s]=f[s];
dfs2(1,0);dfs2(s,0);
for(int re i=1;i<=n;i++)vis[dp[i]]=1;
for(int re i=n+1;i<((n+1)<<1);++i){
if(g[i].size()==1){
int u=i,v=g[u][0];
hash=dp[v]^(f[u]*seed+1);
if(vis[hash])return printf("%d\n",u-n),0;
}
}
}