【BZOJ3899】仙人掌树的同构-圆方树+树上哈希+DP

测试地址:仙人掌树的同构
题目大意:定义一棵仙人掌树为,每个点最多在一个环中的无向图,且图中的环都是简单环。问有多少种点的置换,使得置换后的图和原图相同。n1000
做法:本题需要用到圆方树+树上哈希+DP。
首先显然的是,仙人掌同构就等同于圆方树同构。不过这题的仙人掌定义和一般的仙人掌有些不同:是每个点最多在一个环中,而不是每条边。又因为没有重边,所以没有大小为2的环。通过这个性质我们可以更简单地写出圆方树。为了讨论方便,这里把度数为2的方点都省略,直接将它连接的两个点连接。
我们随便选一个圆点作为根,令f(v)为以点v为根的子树中,当点v确认置换成某一个等价的点时,这棵子树内部有多少种置换。那么:
对于一个圆点,考虑它所有子树的哈希值,如果有k个子树的哈希值相同,那它们的点之间可以互换,因此方案数乘上k!
而对于一个方点,因为环实际上是有顺序的,所以不能像上面一样随便互换。因为环的某一个点已经确定了(该方点的父亲),所以我们只能对这个环做翻转变换,看它和原图是不是同构,如果是,答案就乘上2
在哈希时也要注意,要把圆点和方点区分,要给它们设置不同的哈希参数。而对于方点的哈希,因为我们说过了,环上的点是有顺序的,不能打乱,所以我们把方点的子树正反哈希两遍,取其中的最小值作为方点的哈希值即可。注意写的时候一定要非常注意环上点的顺序。
那这样是不是就完了呢?还没有。注意到我们上面求出的是,选定的根确定的情况下置换的数目,所以我们需要对每个圆点往下都做一遍树哈希,来看有多少个点和选定的根等价,最后再乘上这个点数才是答案。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll mod=1000000003;
const ll P[2]={131,103};
int n,m,first[2010]={0},tfirst[2010]={0},tot=0;
int fa[2010],totpbc,belong[2010];
ll fac[2010],tmp[2010],siz[2010],down[2010],Hash[2010],ans=1;
bool vis[2010]={0};
struct edge
{
    int v,next;
}e[5010],t[5010];

void insert(edge *e,int *first,int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void build(int v)
{
    vis[v]=1;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa[v])
        {
            if (vis[e[i].v])
            {
                if (belong[e[i].v]) continue;
                belong[e[i].v]=++totpbc;
                insert(t,tfirst,e[i].v,totpbc);
                insert(t,tfirst,totpbc,e[i].v);
                for(int p=v;p!=e[i].v;p=fa[p])
                {
                    belong[p]=totpbc;
                    insert(t,tfirst,p,totpbc);
                    insert(t,tfirst,totpbc,p);
                }
            }
            else fa[e[i].v]=v,build(e[i].v);
        }
    if (!belong[v]) belong[v]=++totpbc;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa[v]&&belong[v]!=belong[e[i].v])
        {
            insert(t,tfirst,v,e[i].v);
            insert(t,tfirst,e[i].v,v);
        }
}

void dfs(int v,int fa)
{
    int type=(v>n),to=0;

    siz[v]=1;
    for(int i=tfirst[v];i;i=t[i].next)
    {
        if (t[i].v!=fa)
        {
            dfs(t[i].v,v);
            siz[v]+=siz[t[i].v];
        }
        else to=i;
    }
    tot=0;
    if (to)
    {
        for(int i=t[to].next;i;i=t[i].next)
            tmp[++tot]=down[t[i].v];
    }
    for(int i=tfirst[v];i!=to;i=t[i].next)
        tmp[++tot]=down[t[i].v];

    if (v<=n)
    {
        for(int i=1;i<=tot;i++)
            vis[i]=0;
        for(int i=1;i<=tot;i++)
            if (!vis[i])
            {
                int cnt=0;
                for(int j=i;j<=tot;j++)
                    if (tmp[i]==tmp[j])
                    {
                        vis[j]=1;
                        cnt++;
                    }
                ans=ans*fac[cnt]%mod;
            }
        sort(tmp+1,tmp+tot+1);
        down[v]=0;
        for(int i=1;i<=tot;i++)
            down[v]=down[v]*P[type]+tmp[i];
        down[v]=down[v]*P[type]+siz[v];
        down[v]*=siz[v];
    }
    else
    {
        bool flag=1;
        for(int i=1;i<=tot;i++)
            if (tmp[i]!=tmp[tot-i+1])
            {
                flag=0;
                break;
            }
        if (flag) ans=(ans<<1)%mod;
        down[v]=0;
        for(int i=1;i<=tot;i++)
            down[v]=down[v]*P[type]+tmp[i];
        down[v]=down[v]*P[type]+siz[v];
        down[v]*=siz[v];
        ll now=0;
        for(int i=tot;i>=1;i--)
            now=now*P[type]+tmp[i];
        now=now*P[type]+siz[v];
        now*=siz[v];
        down[v]=min(down[v],now);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert(e,first,a,b);
        insert(e,first,b,a);
    }
    fac[0]=1;
    for(ll i=1;i<=(n<<1);i++)
        fac[i]=fac[i-1]*i%mod;

    tot=0;totpbc=n;
    fa[1]=0;
    build(1);
    dfs(1,0);
    ll same=down[1],cnt=1,now=ans;
    for(int i=2;i<=n;i++)
    {
        dfs(i,0);
        if (same==down[i]) cnt++;
    }
    printf("%llu\n",now*cnt%mod);

    return 0;
}
posted @ 2018-07-09 18:26  Maxwei_wzj  阅读(241)  评论(0编辑  收藏  举报