bzoj4754独特的树叶(树hash)

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

Sample Output

1


第一次打树同构问题,颓了半天题解...其实hash乱搞就行了....
求一棵树的hash, 我们可以 将其儿子的hash值从小到大排序,弄个素数hash一下,最好加上一个依据,不然可能挂掉,时间复杂度 是O(n log n).
无根树同理,我们只要按照某种固定的规则将除了根以外的其他节点的hash值hash掉就可以了.
对于这道题,首先答案只能是入度为1的点,所以我们可以将以第一棵树的节点为根的hash值全部放进一棵Treap里,
接下来把第二棵树hash掉,按标号顺序枚举删去的点,同时在Treap中查找删去这个点后的hash值,如果这个值已经出现过,那么这个节点就是合法答案.
手写的Treap没有set快...
代码
#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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值