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;
}

 

posted @ 2018-11-15 00:54  Gloid  阅读(198)  评论(0编辑  收藏  举报