polya计数回顾

  • burnside引理
    G={g1,g2...gk},k=|G| 是[1,n]上的置换群,令 c(gi) 表示置换 gi 下不动点的个数,那么G将[1,n]划分为等价类>的个数是:

    l=1|G|j=1|G|c(gi)

    即是说,等价类个数等于不动点个数的均值。
    如果存在 gi gi(a)>b 那么我们就说观测到a和b等价。换句话说,我们看到的a和b是同一个东西。

  • polya定理
    G={g1,g2...gk},k=|G| 是[1,n]上的置换群,令 C(gi) 表示置换 gi 下循环节的个数,现在用m个颜色对[1,n]着色,那么在G作用下,[1,n]的不同着色方案数为:

    l=1|G|j=1|G|mC(gi)

  • polya定理是burnside的特例。
    需要注意的是,burnside算的是 [1,n] n 个数的不同等价类的个数;polya算的是mn个方案数中不同等价类的个数。因此,burnside中的 [1,n] 是计数对象,而polya中的方案数是计数对象。
    burnside中的 g 直接作用于计数对象[1,n];而polya中的 g 虽然也作用于[1,n],但 [1,n] 不是计数对象,方案数才是,但是 g 通过作用到[1,n]间接作用到方案数上面。两式对比,容易发现 mC(gi) 是burnside中的不动点数
    比如 n=4,g=(123)(4) ,着色方案为 f=(abcd), 那么 g(f)=(cabd) 。不动点是自己映射到自己,即 g(f)=f 。对于上例就是 (cabd)=(abcd), 于是有 c=a,a=b,b=c,d=d 。因此如果方案 f g的不动点,那么在 g 的每个循环节内,f只能染一种颜色。换句话说,用 f 染完色后,g的循环节内的点颜色相同。对于循环节为 C(gi) 的置换,其不动点个数为 mC(gi)

顺便上一题巩固一下
[HNOI2008]Cards
这里不能直接套用polya,因为每种颜色的使用量是有限制的,因此考虑用burnside计算。对于置换g,因为同一循环节里面只能用同一种颜色,因此我们统计出g的第i个循环节里面有ci个数。然后我们用这些数dp就可以了。
dp[i][r][g][b]irrggbb, 那么:

dp[i][r][g][b]=dp[i][rci][g][b]+dp[i][r][g][bci]+dp[i][r][gci][b]

/**************************************************************
    Problem: 1004
    User: 63116021
    Language: C++
    Result: Accepted
    Time:120 ms
    Memory:3616 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define clr(A) memset(A,0,sizeof(A))
using namespace std;

const int MM = 10000;

int r,b,g,m,p;
int fg = 0;
typedef struct GG{
    int x,y;
    GG(int _x,int _y){
    x = _x,y = _y;
    }
}G;

G Ext_gcd(int a,int b)
{
    if(b==0) return G(1,0);
    G tmp = Ext_gcd(b,a%b);
    return G(tmp.y,tmp.x-a/b*tmp.y);
}



int getcir(int a[],int nn)
{
    int hash[nn+3];
    int B[nn+2];
    clr(hash);
    int res = 0;
    for(int i = 1;i<=nn;i++)
    if(!hash[i])
    {
        B[++res] = 1;
        hash[i] = 1;
        int tmp = a[i];
        while(tmp!=i)
        {
            hash[tmp] = 1,B[res]++,tmp = a[tmp];
        }
    }

    int F[res+2][b+2][r+2][g+2];
    clr(F);
    F[0][0][0][0] = 1;
    for(int i = 1;i<=res;i++)
    for(int sb = 0;sb<=b;sb++)
    for(int sr = 0;sr<=r;sr++)
    for(int sg = 0;sg<=g;sg++)
    //if(sb+sr+sg>=B[i])
    {
      if(sb>=B[i]) F[i][sb][sr][sg] += F[i-1][sb-B[i]][sr][sg];
      if(sr>=B[i]) F[i][sb][sr][sg] += F[i-1][sb][sr-B[i]][sg];
      if(sg>=B[i]) F[i][sb][sr][sg] += F[i-1][sb][sr][sg-B[i]];
      F[i][sb][sr][sg] %= p;
    }
    if(res == nn) fg = 1;
    return F[res][b][r][g];
}
int main()
{
    //freopen("Pr_temp.in","r",stdin);
    scanf("%d%d%d%d%d",&r,&b,&g,&m,&p);
    int n = r+b+g;
    int A[n+2];
    int ans = 0;
    for(int i = 1;i<=m;i++)
    {
        for(int j = 1;j<=n;j++)
        scanf("%d",A+j);
        ans = (ans+getcir(A,n)) % p;
    }
    if(!fg){
        for(int i = 1;i<=n;i++)
        A[i] = i;
        ans = (ans+getcir(A,n)) % p;
        m++;
    }
    G Gg = Ext_gcd(m,p);
    int Inv = (Gg.x+p) % p;
    printf("%d\n",Inv*ans%p);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值