独特的树叶
1695:独特的树叶
时间限制: 1000 ms 内存限制: 262144 KB
【题目描述】
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中编号最小的那一个的编号。
【数据规模】
1<=n<=1e5.
【题解】
判断两棵无根树是否同构,则需在它们中分别有一个根u,和根v,使得树A以u为根的哈希值等于B以v为根的哈希值。
那么我们只需要求出A树以每一个点为根的哈希值装入map中,在判断B去掉了一个叶子节点(入出度为1)后以任一根的哈希值是否在map中即可。
考虑有根树哈希值。
这里的公式为:
将u的子节点按f值大小排序,令ch[i]=131的i次方。
f[u]=siz[u]*∑f[v]*ch[i-1];(i:1--u子节点个数)
那么我们可以在O(nlogn)时间内求出以1号点为根的所有f值。
现在要求出以任一点为根的根的f值.
考虑换根树形DP。
设g[u]为以u的父亲为根时,去掉u及其子树中所有点后的哈希值。(注意是以u的父亲为根)
则对于u的儿子节点
令hu数组表示除去f[v]外的u所有儿子的f值,如果u不为1,则加入g[u].
g[v]=(n-siz[v])∑hu[i]*ch[i-1]。
排序后对hu数组求一个前缀和和后缀和即可O(1)求出每个g[v]。
ans[u]表示以u为根的整棵树哈希值。
ans[u]即为g[v]式子不去掉f[v]所求出的值。
将每个ans加入map中。
对于B树的每个叶节点,去掉它后树的哈希值则为以它为根节点的ans/(n+1) 取质数模数求逆元可得 (有哈希定义得到)
判断得求出的值在map中就输出。
代码如下:
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+5; const int mo=1e9+9; const int cc=131; int gsn,huan[N],pre[N],sub[N],ch[N]; map <int,int> ma; struct pigu { int dao,ne; }; struct choujiao { int hao,zhi; }dahuan[N]; inline bool cmp(choujiao x,choujiao y) { return x.zhi<y.zhi; } struct tree { int n,size,last[N],siz[N],cnt[N],g[N]; int f[N],ans[N]; pigu a[N<<1]; inline void lingjiebiao(int x,int y) { a[++size].dao=y; a[size].ne=last[x]; last[x]=size; cnt[x]++; } inline void get_f(int now,int fa) { siz[now]=1; int cnt=0; for(int i=last[now];i;i=a[i].ne) { if(a[i].dao==fa) continue; get_f(a[i].dao,now); siz[now]+=siz[a[i].dao]; } for(int i=last[now];i;i=a[i].ne) { if(a[i].dao==fa) continue; huan[++cnt]=f[a[i].dao]; } sort(huan+1,huan+cnt+1); for(int i=1;i<=cnt;i++) f[now]+=(huan[i]*ch[i-1])%mo; f[now]*=siz[now];f[now]%=mo; if(!cnt) f[now]=1; } inline void get_ans(int now,int fa) { int cnt=0; for(int i=last[now];i;i=a[i].ne) { if(a[i].dao==fa) continue; dahuan[++cnt]={a[i].dao,f[a[i].dao]}; } if(fa) dahuan[++cnt]={-1,g[now]}; sort(dahuan+1,dahuan+cnt+1,cmp); pre[0]=sub[cnt+1]=0; for(int i=1;i<=cnt-1;i++) pre[i]=(pre[i-1]+(dahuan[i].zhi*ch[i-1])%mo)%mo; for(int i=cnt;i>=2;i--) sub[i]=(sub[i+1]+(dahuan[i].zhi*ch[i-2])%mo)%mo; for(int i=1;i<=cnt;i++) { if(dahuan[i].hao==-1) continue; g[dahuan[i].hao]=((n-siz[dahuan[i].hao])*(pre[i-1]+sub[i+1])%mo+mo)%mo; g[dahuan[i].hao]%=mo; } if(!fa&&cnt==1) g[dahuan[1].hao]=1; for(int i=1;i<=cnt;i++) ans[now]=(ans[now]+dahuan[i].zhi*ch[i-1]%mo)%mo; ans[now]*=n;ans[now]%=mo; for(int i=last[now];i;i=a[i].ne) if(a[i].dao!=fa) get_ans(a[i].dao,now); } }t1,t2; inline int read() { char c=getchar(); int x=0,f=1; while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();} while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();} return x*f; } inline int hapow(int ch,int ci) { int x=ch,gs=1; while(ci) { if(ci&1) gs=(gs*x)%mo; x=(x*x)%mo; ci>>=1; } return gs; } signed main() { gsn=read();ch[0]=1; for(int i=1;i<=gsn+2;i++) ch[i]=(ch[i-1]*cc)%mo; t1.n=gsn;t2.n=gsn+1; for(int i=1,x,y;i<=gsn-1;i++) { x=read();y=read(); t1.lingjiebiao(x,y); t1.lingjiebiao(y,x); } for(int i=1,x,y;i<=gsn;i++) { x=read();y=read(); t2.lingjiebiao(x,y); t2.lingjiebiao(y,x); } t1.get_f(1,0);t2.get_f(1,0); t1.get_ans(1,0);t2.get_ans(1,0); for(int i=1;i<=gsn;i++) ma[t1.ans[i]]=1; for(int i=1;i<=gsn+1;i++) { if(t2.cnt[i]>1) continue; if(ma[(t2.ans[i]*hapow(gsn+1,mo-2))%mo]==1) { cout<<i; return 0; } } }