UOJ Logo zhouzixuan的博客

博客

bzoj 4337: BJOI2015 树的同构

2015-11-26 22:28:12 By zhouzixuan
【题目描述】:
    树是一种很常见的数据结构。
    我们把N个点,N-1条边的连通无向图称为树。
    若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。
    对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相同,那么这两个树是同构的。也就是说,它们具有相同的形态。现在,给你M个有根树,请你把它们按同构关系分成若干个等价类。
【题目解法】:
    首先很容易想到枚举每棵树的每个点作为根,然后判断是否重构
    现在问题转化为判断两个有根树是否重构
    解法一:最小表示法。我们用括号序列表示一棵树,每次我们求以一个点为根的子树的最小表示,我们可以划分为求其儿子的最小表示,然后把这些括号序列排序再串起来,两边补一个()即可。然后我们可以对每一颗树求出最小字典序的最小表示法,然后判断即可
    解法二:hash。其实和上面差不多,只不过需要一个比较好的hash函数即可
    复杂度O(N^3)
    下面代码中给出了两种解法
    其中hash快一点
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
const int N=55;
struct edge {int x,next;} b[N*2];
int m,n,tot,a[N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline void addedge(int x,int y)
{
    ++tot;
    b[tot].x=y;
    b[tot].next=a[x];
    a[x]=tot;
}
void init()
{
    m=getint();
}
namespace MinRepresent
{
    string s[N],ans[N],son[N];
    void init()
    {
        n=getint(); tot=0; memset(a,0,sizeof(a));
        for (int j=1; j<=n; j++)
        {
            int x=getint();
            if (x!=0) addedge(j,x),addedge(x,j);
        }
    }
    void dfs(int x,int fa)
    {
        s[x]='('; int cnt=0;
        for (int p=a[x];p;p=b[p].next) if (b[p].x!=fa) dfs(b[p].x,x);
        for (int p=a[x];p;p=b[p].next) if (b[p].x!=fa) son[++cnt]=s[b[p].x];
        if (cnt>0) sort(son+1,son+cnt+1);
        for (int i=1; i<=cnt; i++) s[x]+=son[i];
        s[x]+=')';
    }
    void solve()
    {
        for (int i=1; i<=m; i++)
        {
            init();
            for (int j=1; j<=n; j++)
            {
                dfs(j,0); if (j==1) {ans[i]=s[j]; continue;}
                if (s[j]<ans[i]) ans[i]=s[j];
            }
        }
        for (int i=1; i<=m; i++)
        {
            int nowans=i;
            for (int j=1; j<i; j++) if (ans[i]==ans[j]) nowans=min(nowans,j);
            printf("%d\n",nowans);
        }
    }
}
namespace Hash
{
    unsigned long long H[N],hash[N],ans[N];
    void init()
    {
        n=getint(); tot=0; memset(a,0,sizeof(a));
        for (int j=1; j<=n; j++)
        {
            int x=getint();
            if (x!=0) addedge(j,x),addedge(x,j);
        }
    }
    void dfs(int x,int fa,int dep)
    {
        unsigned long long now=H[dep];
        for (int p=a[x];p;p=b[p].next)
        {
            int pp=b[p].x; if (pp==fa) continue;
            dfs(pp,x,dep+1); now=now+hash[pp]*H[dep];
        }
        hash[x]=now*now;
    }
    void solve()
    {
        srand(19990315);
        for (int i=1; i<=50; i++) H[i]=rand();
        for (int i=1; i<=m; i++)
        {
            init();
            for (int j=1; j<=n; j++)
            {
                dfs(j,0,1); if (j==1) {ans[i]=hash[j]; continue;}
                if (hash[j]<ans[i]) ans[i]=hash[j];
            }
        }
        for (int i=1; i<=m; i++)
        {
            int nowans=i;
            for (int j=1; j<i; j++) if (ans[i]==ans[j]) nowans=min(nowans,j);
            printf("%d\n",nowans);
        }
    }
}
int main()
{    
    init();
    Hash::solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。