BZOJ4754 JSOI2016独特的树叶(哈希)
判断两棵无根树是否同构只需要把重心提作根哈希即可。由于只添加了一个叶子,重心的位置几乎不发生偏移,所以直接把两棵树的重心提起来,逐层找哈希值不同且对应的两子树即可。被一个普及组子问题卡一年。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #define ul unsigned long long #define N 100010 char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,ans,f[N],g[N]; struct tree { int p[N],t,size[N],root; ul hax[N],f[N]; struct data{int to,nxt;}edge[N<<1]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void dfs(int k,int from) { size[k]=1; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) dfs(edge[i].to,k),size[k]+=size[edge[i].to]; } int findroot(int k,int s) { int mx=0; for (int i=p[k];i;i=edge[i].nxt) if (size[edge[i].to]<size[k]&&size[edge[i].to]>size[mx]) mx=edge[i].to; if (size[mx]*2>s) return findroot(mx,s); else return k; } void refind() { for (int i=p[root];i;i=edge[i].nxt) if (size[edge[i].to]*2>=size[root]) {root=edge[i].to;break;} } void gethash(int k,int from) { for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) gethash(edge[i].to,k); int cnt=0; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) f[++cnt]=hax[edge[i].to]; sort(f+1,f+cnt+1); hax[k]=0; for (int i=1;i<=cnt;i++) hax[k]=hax[k]*107+f[i]; hax[k]+=size[k]*509; } }a,b; bool cmp(const int&x,const int&y) { return a.hax[x]<a.hax[y]; } bool cmp2(const int&x,const int&y) { return b.hax[x]<b.hax[y]||b.hax[x]==b.hax[y]&&x>y; } void work(int x,int y) { int cnt=0,cnt2=0; for (int i=a.p[x];i;i=a.edge[i].nxt) if (a.size[a.edge[i].to]<a.size[x]) f[++cnt]=a.edge[i].to; for (int i=b.p[y];i;i=b.edge[i].nxt) if (b.size[b.edge[i].to]<b.size[y]) g[++cnt2]=b.edge[i].to; sort(f+1,f+cnt+1,cmp); sort(g+1,g+cnt2+1,cmp2); if (cnt>cnt2||cnt2>cnt+1) return; if (cnt2==cnt+1) { int t=0; for (int i=1,j=1;i<=cnt2;i++,j++) if (a.hax[f[j]]!=b.hax[g[i]]||j>cnt) if (!t) t=g[i++];else return; if (t) ans=min(ans,t); return; } if (cnt2==cnt) { int u,v; for (u=1;u<=cnt;u++) if (a.hax[f[u]]!=b.hax[g[u]]) break; if (!u) return; if (a.hax[f[u]]<b.hax[g[u]]) for (v=u;v<cnt;v++) if (a.hax[f[v+1]]!=b.hax[g[v]]) break; if (!v) return; for (int i=v+1;i<=cnt;i++) if (a.hax[f[i]]!=b.hax[g[i]]) return; for (int i=1;i<=cnt;i++) if (b.hax[g[v]]==b.hax[g[i]]) work(f[u],g[i]); } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4754.in","r",stdin); freopen("bzoj4754.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read();ans=n+1; for (int i=1;i<n;i++) { int x=read(),y=read(); a.addedge(x,y),a.addedge(y,x); } for (int i=1;i<=n;i++) { int x=read(),y=read(); b.addedge(x,y),b.addedge(y,x); } a.dfs(1,1),b.dfs(1,1); a.root=a.findroot(1,n),b.root=b.findroot(1,n+1); a.dfs(a.root,a.root),b.dfs(b.root,b.root); a.gethash(a.root,a.root),b.gethash(b.root,b.root); work(a.root,b.root); if (n&1) a.refind(); else b.refind(); a.dfs(a.root,a.root),b.dfs(b.root,b.root); a.gethash(a.root,a.root),b.gethash(b.root,b.root); work(a.root,b.root); cout<<ans; return 0; }