【置换群&Polya定理】Sultan's Chandelier UVA - 11540

题意:

给出一棵树,用m种颜色给树上的每一个节点染色,求不同的染色方案。两个染色方案视为相同,当且仅当子树内的节点经过旋转或不旋转后相同。旋转:这棵树长得非常怪异,在以u为根的子树,u的一代子节点形成了一个类似环状的结构;旋转一个单位后,原来排在第一位的子树排在了最后,原来排在第二的子树排在了第一。

思路:

先简单的介绍一下Polya定理
G = { p 1 , p 2 , , p g } 是n个对象的一个置换群,记 c ( p i ) 为置换 p i 的循环节数, 用m种颜色染这n个对象,则不同的染色方案数为:
L = i = 1 g m c ( p i ) | G |
当然,如果这并不是一个单纯的染色,可能涉及到一些限制时,你可以将 m c ( p i ) 替代为在置换 p i 下染色的方案数 f ( i ) ,由于不同循环间相互独立,而同一循环中的元素染色相同,所以 f ( i ) 等于每一个循环的染色方案数的乘积。
我们单独考虑一棵子树,对于这棵子树的一代子节点能产生哪一些置换。首先,肯定能产生不动置换,接下来考虑旋转 i 个单位后能否产生的置换。若能产生置换,那么在同一个循环中的节点的子树一定具有相同的形态(可以是旋转后具有相同的形态),所以,要处理出tg[u][v],表示子树u与子树v是否具有相同的形态。当我们知道有哪些置换后就可以套用公式了。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 110
#define MO 1000000007
#define LL long long
int c,n,G[MAXN][MAXN],l;
char s[MAXN*20];
LL dp[MAXN],inv[MAXN*MAXN];
void Init(int u)//构造树
{
    G[u][0]=0;
    while(s[l]=='[')
    {
        if(s[l]=='[')
        {
            G[u][++G[u][0]]=++n;
            l++,Init(n);
        }
        l++;
        if(s[l]==',') l++;
    }
}
int tg[MAXN][MAXN],vis[MAXN];
int check(int u,int v)//检查u,v两棵子树的形态是否相同
{
    if(tg[u][v]) return tg[u][v];
    if(G[u][0]!=G[v][0]) return tg[u][v]=tg[v][u]=-1;
    if(G[u][0]==0) return tg[u][v]=tg[v][u]=1;
    for(int i=0;i<G[u][0];i++)
    {
        bool f=1;
        for(int j=1;j<=G[v][0];j++)
        {
            int k=i+j;
            if(k>G[v][0]) k-=G[v][0];
            if(check(G[u][k],G[v][j])==-1)
            {
                f=0;
                break;
            }
        }
        if(f==1) return tg[u][v]=tg[v][u]=1;
    }
    return tg[u][v]=tg[v][u]=-1;
}
void Solve(int u)
{
    dp[u]=(u==1)?1:c;
    for(int i=1;i<=G[u][0];i++)
    {
        Solve(G[u][i]);
        dp[u]*=dp[G[u][i]];
        dp[u]%=MO;
    }
    int cnt=1;//记录有多少个置换
    for(int i=1;i<G[u][0];i++)//枚举旋转多少个单位
    {
        memset(vis,0,sizeof vis);
        bool flag=1;
        LL num=c;
        for(int j=1;j<=G[u][0];j++)
            if(!vis[j])
            {
                int k=j;
                do
                {
                    vis[k]=1;
                    if(check(G[u][k],G[u][j])==-1)
                    {
                        flag=0;
                        break;
                    }
                    k+=i;
                    if(k>G[u][0]) k-=G[u][0];
                }while(!vis[k]);
                if(flag==0) break;
                num*=dp[G[u][j]];
                num%=MO;
            }
        if(flag)
        {
            cnt++;
            dp[u]=(dp[u]+num)%MO;
        }
    }
    dp[u]=dp[u]*inv[cnt]%MO;
}
int main()
{
    inv[1]=1;
    for(int i=2;i<MAXN*MAXN;i++)
        inv[i]=1LL*(MO-MO/i)*inv[MO%i]%MO;
    int tm;
    scanf("%d",&tm);
    for(int cs=1;cs<=tm;cs++)
    {
        memset(tg,0,sizeof tg);
        scanf("%s%d",s,&c);
        n=0,l=0;
        Init(++n);
        Solve(1);
        printf("Case #%d: %d\n",cs,dp[1]);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_41343943/article/details/82527770