[bzoj4474][Jsoi2015]isomorphism【树哈希】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=4474
【题解】
  先求出重心,(如果有两个重心就再新建一个点作为新重心),将无根树转化为有根树。然后再对有根树进行哈希。对于一棵子树,它的哈希值的计算方法是:
  1.求出树根节点的每个儿子的子树的哈希值。
  2.将这些哈希值排序。
  3.每次加上一个儿子的哈希值乘以进制D + 常数 C。
  4.将得到的结果开平方。
  可以证明,这种构造哈希函数的方法是比较合理的。
  时间复杂度 : O(NlogN)
【代码】

/* --------------
    user Vanisher
    problem bzoj-4474 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       10010
# define    M       21
using namespace std;
int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
const ll P=998244353, S=20011216, Q=19260817, B=2333333;
struct Edge{
    int data,next;
}e[N*2];
struct Node{
    int num; ll sum;
}fin[M];
int m, n, deg[N], head[N], place, id, nexv[N], nexu[N], p[N], num, size[N], mx[N], tmp, rt;
ll now[N], h[N];
void build(int u, int v){
    e[++place].data=v; e[place].next=head[u]; head[u]=place;
}
bool cmp(Node x, Node y){
    return x.sum<y.sum;
}
void del(int x, int fa, int an){
    if (deg[x]!=2){
        if (an!=0) nexu[++num]=x, nexv[num]=an;
        for (int ed=head[x]; ed!=0; ed=e[ed].next)
            if (e[ed].data!=fa) del(e[ed].data,x,x);
    }
    else {
        for (int ed=head[x]; ed!=0; ed=e[ed].next)
            if (e[ed].data!=fa) del(e[ed].data,x,an);
    }
} 
void heave(int x, int fa){
    size[x]=1; mx[x]=0;
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa){
            heave(e[ed].data,x);
            size[x]=size[x]+size[e[ed].data];
            mx[x]=max(mx[x],size[e[ed].data]);
        }
    mx[x]=max(mx[x],tmp-size[x]);
}
void solve(int x, int fa){
    int cnt=0;
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa) solve(e[ed].data,x);
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa) now[++cnt]=h[e[ed].data];
    sort(now+1,now+cnt+1);
    h[x]=S;
    for (int i=1; i<=cnt; i++) h[x]=(h[x]*Q+now[i]+B)%P;
    h[x]=(h[x]*h[x])%P;
}
int main(){
    m=read();
    for (int t=1; t<=m; t++){
        n=read();
        memset(head,0,sizeof(head)); place=0;
        memset(deg,0,sizeof(deg));
        for (int i=1; i<n; i++){
            int u=read(), v=read();
            build(u,v), build(v,u);
            deg[u]++, deg[v]++; 
        }
        tmp=n;
        for (int i=1; i<=n; i++){
            if (deg[i]==1) rt=i;
            if (deg[i]==2) tmp--;
        }
        num=0; del(rt,0,0);
        memset(head,0,sizeof(head)); place=0;       
        for (int i=1; i<=num; i++){
            build(nexu[i],nexv[i]);
            build(nexv[i],nexu[i]);
        }
        heave(rt,0);
        int mn=inf, mnsize=0;
        for (int i=1; i<=n; i++){
            if (deg[i]==2) continue;
            if (mn>mx[i]) mn=mx[i], mnsize=0;
            if (mn==mx[i]) p[++mnsize]=i;
        }
        if (mnsize==2){
            memset(head,0,sizeof(head));
            for (int i=1; i<=num; i++){
                if ((nexu[i]==p[1]&&nexv[i]==p[2])||(nexu[i]==p[2]&&nexv[i]==p[1])){
                    build(n+1,nexu[i]), build(nexu[i],n+1);
                    build(n+1,nexv[i]), build(nexv[i],n+1);
                }
                else {
                    build(nexu[i],nexv[i]);
                    build(nexv[i],nexu[i]);
                }
            }
            rt=n+1;
        }
        else rt=p[1];
        solve(rt,0); 
        fin[t].num=tmp; fin[t].sum=h[rt];
    }
    sort(fin+1,fin+m+1,cmp);
    int cnt=0;
    for (int i=1; i<=m; i++)
        if (fin[i].sum!=fin[i-1].sum)
            size[++cnt]=fin[i].num;
    sort(size+1,size+cnt+1);
    printf("%d\n",cnt);
    for (int i=1; i<=cnt; i++)
        printf("%d%c",size[i],(i==cnt)?('\n'):(' '));
    return 0;
}
posted @ 2018-05-21 22:17  Vanisher  阅读(146)  评论(0编辑  收藏  举报