Description
JYY有两棵树A和B:树A有N个点,编号为1到N;树B有N+1个点,编号为1到N+1。JYY知道树B恰好是由树A加上一个叶
节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是树B中的哪一个叶节点呢?
Input
输入一行包含一个正整数N。
接下来N-1行,描述树A,每行包含两个整数表示树A中的一条边;
接下来N行,描述树B,每行包含两个整数表示树B中的一条边。
1≤N≤10^5
Output
输出一行一个整数,表示树B中相比树A多余的那个叶子的编号。如果有多个符合要求的叶子,输出B中编号最小的
那一个的编号
Sample Input
5
1 2
2 3
1 4
1 5
1 2
2 3
3 4
4 5
3 6
1 2
2 3
1 4
1 5
1 2
2 3
3 4
4 5
3 6
Sample Output
1
第一次打树同构问题,颓了半天题解...其实hash乱搞就行了....
求一棵树的hash,
我们可以
将其儿子的hash值从小到大排序,弄个素数hash一下,最好加上一个依据,不然可能挂掉,时间复杂度
是O(n log n).
无根树同理,我们只要按照某种固定的规则将除了根以外的其他节点的hash值hash掉就可以了.
对于这道题,首先答案只能是入度为1的点,所以我们可以将以第一棵树的节点为根的hash值全部放进一棵Treap里,
接下来把第二棵树hash掉,按标号顺序枚举删去的点,同时在Treap中查找删去这个点后的hash值,如果这个值已经出现过,那么这个节点就是合法答案.
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100005
#define maxx 200005
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{ char c=getchar();int x=0,y=1;
while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
typedef unsigned int ull;
const ull P=123;
int cnt;
ull xp[maxn],q[maxn];
struct node
{ int id;ull val;
node(ull x=0,int y=0):val(x),id(y){}
friend bool operator <(const node& a,const node& b){return a.val<b.val;}
}Q[maxn];
struct tree
{ struct road{int en,nex;}ro[maxx];
int num,n,hea[maxn],fa[maxn],s[maxn],rin[maxn];
ull Hash[maxn],f[maxn],suf[maxn],pre[maxn],rval[maxn];
void add(int x,int y){ro[++num].en=y;ro[num].nex=hea[x];hea[x]=num;}
void init()
{ num=0;int x,y;mem(hea,0);f[1]=0;mem(s,0);mem(suf,0);mem(pre,0);mem(rin,0);mem(f,0);mem(Hash,0);mem(rval,0);
for(int i=1;i<n;++i){x=read();y=read();add(x,y);add(y,x);++rin[x];++rin[y];}
}
void dfs(int x)
{ s[x]=1;
for(int i=hea[x];i;i=ro[i].nex)
{ int v=ro[i].en;
if(v==fa[x]) continue;
fa[v]=x;dfs(v);
s[x]+=s[v];
}
cnt=0;
for(int i=hea[x];i;i=ro[i].nex) if(fa[ro[i].en]==x) q[++cnt]=Hash[ro[i].en];
sort(q+1,q+cnt+1);Hash[x]=0;
for(int i=1;i<=cnt;++i) Hash[x]=Hash[x]*P+q[i];
Hash[x]=Hash[x]*P+s[x];
}
void dfs2(int x)
{ cnt=0;
if(x>1) Q[++cnt]=node(f[x],fa[x]);
for(int i=hea[x];i;i=ro[i].nex)
{ int v=ro[i].en;
if(v!=fa[x]) Q[++cnt]=node(Hash[v],v);
}
sort(Q+1,Q+cnt+1);
for(int i=1;i<=cnt;++i) pre[i]=pre[i-1]*P+Q[i].val;
suf[cnt+1]=0;
for(int i=cnt;i>=1;--i) suf[i]=suf[i+1]+Q[i].val*xp[cnt-i];
for(int i=1;i<=cnt;++i)
if(Q[i].id!=fa[x])
{ f[Q[i].id]=pre[i-1]*xp[cnt-i]+suf[i+1];
f[Q[i].id]=f[Q[i].id]*P+n-s[Q[i].id];
}
for(int i=hea[x];i;i=ro[i].nex) if(fa[ro[i].en]==x) dfs2(ro[i].en);
}
void solve()
{ dfs(1);dfs2(1);
for(int i=1;i<=n;++i)
{ cnt=0;
for(int j=hea[i];j;j=ro[j].nex)
if(fa[ro[j].en]==i) q[++cnt]=Hash[ro[j].en];
if(fa[i]) q[++cnt]=f[i];
sort(q+1,q+cnt+1);
rval[i]=0;
for(int j=1;j<=cnt;++j) rval[i]=rval[i]*P+q[j];
rval[i]=rval[i]*P+n;
}
}
}A,B;
struct Treap
{ int r,s;ull v;Treap *ch[2];
Treap(ull x):v(x),r(rand()),s(1){ch[0]=ch[1]=NULL;}
}*root;
inline void rotate(Treap* &o,int d){Treap *k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;o=k;}
inline void insert(Treap* &o,ull x)
{ if(!o){o=new Treap(x);return;}
if(x==o->v) return;
int d=x> o->v;
insert(o->ch[d],x);
if(o->ch[d]->r > o->r) rotate(o,d^1);
}
inline bool find(Treap* o,ull x)
{ if(!o) return 0;
if(o->v==x) return 1;
if(x<o->v) return find(o->ch[0],x);
return find(o->ch[1],x);
}
int main()
{ int n;
n=read();xp[0]=1;
for(int i=1,j=n+2;i<=j;++i) xp[i]=xp[i-1]*P;
A.n=n;A.init();B.n=n+1;B.init();
A.solve();B.solve();
for(int i=1;i<=n;++i) insert(root,A.rval[i]);
for(int i=1,j=n+1;i<=j;++i)
if(B.rin[i]==1&&((i!=1&&find(root,B.f[i]))||(i==1&&find(root,B.Hash[B.ro[B.hea[i]].en]))))
{printf("%d",i);break;}
return 0;
}