zoukankan      html  css  js  c++  java
  • 【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
    ",now*cnt%mod);
    
        return 0;
    }
  • 相关阅读:
    docker 基本概念
    6_State 游戏开发中使用状态机
    5_Singleton 游戏开发中的单例模式
    4_Prototype 原型
    3_observer
    2_flyweight, 轻量化模式
    1_Command 游戏开发命令模式
    CentOS7 Failed to start LSB: Bring up/down解决方法
    CentOS 7 中firewall-cmd命令
    CentOS查询端口占用和清除端口占用的程序
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793292.html
Copyright © 2011-2022 走看看