Description
nodgd家里种了一棵树,有一天nodgd比较无聊,就把这棵树画在了一张纸上。另一天nodgd更无聊,就又画了一张。
这时nodgd发现,两次画的顺序是不一样的,这就导致了原本的某一个节点u0在第一幅图中编号为u1,在第二副图中编号为u2。
于是,nodgd决定检查一下他画出的两棵树到底是不是一样的。nodgd已经给每棵树的节点都从1到n进行了编号,即每棵树n个节点。
如果存在一个1到n的排列p1p2…pn,对于第一幅图中的任意一条边(u,v),在第二幅图中都能找到一条边(pu,pv),则认为这两幅图中的树是一样的。
知道是树hash,但是从来没写过这个玩意儿,没有信心写出来,然后想了个水法只捞到10分。。
其实是树hash的模板题。。无根树转有根树是经典操作了,把重心当根,超过1个重心就直接新建一个点做为根,注意要把重心拉上去,重心之间的边显然不能走。然后处理完两棵树以后直接树hash,具体的话就是:
(((hash[x]*=base)^=tmp[i])+=tmp[i])^=tmp[i];
从下往上hash就可以了,注意base不能太小,最好不要模,冲突可能性比较大,还是自然溢出比较好,。
然后方案的话扫一遍用hash来映射就ok了。
一个小错误导致WA半天。。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define mp make_pair
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m;
const int N=4e5+5;
typedef long long ll;
const ll base=1e9+7;
int root,root1,root2;
int tot,head[N],f[N],size[N],ans[N],next[N],go[N],del[N];
ll hash[N],tmp[N];
pair<ll,int>q1[N],q2[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add(int x,int y)
{
go[++tot]=y;next[tot]=head[x];head[x]=tot;
go[++tot]=x;next[tot]=head[y];head[y]=tot;
}
inline void getroot(int x,int fa)
{
size[x]=1;
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (v==fa||del[i])continue;
getroot(v,x);
size[x]+=size[v];
f[x]=max(f[x],size[v]);
}
f[x]=max(f[x],n-size[x]);
if (!root||f[x]<f[root])root=x;
}
inline int gethash(int x,int fa)
{
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (v==fa||del[i])continue;
gethash(v,x);
}
int cnt=0;
for(int i=head[x];i;i=next[i])
{
int v=go[i];
if (v!=fa&&!del[i])tmp[++cnt]=hash[v];
}
sort(tmp+1,tmp+cnt+1);
hash[x]=base;
fo(i,1,cnt)
(((hash[x]*=base)^=tmp[i])+=tmp[i])^=tmp[i];
}
inline void solve1()
{
fo(i,1,n-1)
{
int x,y;
x=read(),y=read();
add(x,y);
}
root=0;
int r1=0,r2=0;
getroot(1,0);
fo(i,1,n)if (f[i]==f[root])r2=r1,r1=i;
if (r2)
{
for(int i=2;i<=tot;i+=2)
{
int v=go[i];
if (go[i]==r1&&go[i^1]==r2||go[i]==r2&&go[i^1]==r1)
{
del[i]=del[i^1]=1;
break;
}
}
add(n*2+1,r1);add(n*2+1,r2);
root=n*2+1;
}
root1=root;
gethash(root,0);
}
inline void solve2()
{
fo(i,1,n-1)
{
int x,y;
x=read(),y=read();
add(x+n,y+n);
}
root=0;
int r1=0,r2=0;
getroot(n+1,0);
fo(i,n+1,n+n)
if (f[i]==f[root])r2=r1,r1=i;
if (r2)
{
for(int i=2;i<=tot;i+=2)
{
int v=go[i];
if (go[i]==r1&&go[i^1]==r2||go[i]==r2&&go[i^1]==r1)
{
del[i]=del[i^1]=1;
break;
}
}
add(n*2+2,r1);add(n*2+2,r2);
root=n*2+2;
}
root2=root;
gethash(root,0);
}
inline void getans(int x1,int x2,int fa1,int fa2)
{
int tot1=0,tot2=0;
for(int i=head[x1];i;i=next[i])
{
int v=go[i];
if (v!=fa1&&!del[i])q1[++tot1]=mp(hash[v],v);
}
for(int i=head[x2];i;i=next[i])
{
int v=go[i];
if (v!=fa2&&!del[i])q2[++tot2]=mp(hash[v],v);
}
sort(q1+1,q1+1+tot1);
sort(q2+1,q2+1+tot2);
fo(i,1,tot1)ans[q1[i].second]=q2[i].second;
for(int i=head[x1];i;i=next[i])
{
int v=go[i];
if (v!=fa1&&!del[i])getans(v,ans[v],x1,x2);
}
}
int main()
{
freopen("check.in","r",stdin);
freopen("check.out","w",stdout);
scanf("%d",&n);
tot=1;
solve1();
solve2();
if (hash[root1]!=hash[root2])
{
printf("NO\n");
return 0;
}
ans[root1]=root2;
getans(root1,root2,0,0);
printf("YES\n");
fo(i,1,n)printf("%d ",ans[i]-n);
return 0;
}