kyeremal-bzoj1004-[HNOI2008]-cards-dp+polya

2 篇文章 0 订阅
1 篇文章 0 订阅

bzoj1004-[HNOI2008]-cards

F.A.QsHomeDiscussProblemSetStatusRanklistContestModifyUser   ManacherLogout捐赠本站
Notice:求历年World Final数据,谢谢 & OJ试题突破3000大关!

1004: [HNOI2008]Cards

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 2122   Solved: 1262
[ Submit][ Status][ Discuss]

Description

小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

Input

第一行输入 5 个整数:Sr,Sb,Sg,m,p(m<=60,m+1<p<100)。n=Sr+Sb+Sg。接下来 m 行,每行描述
一种洗牌法,每行有 n 个用空格隔开的整数 X1X2...Xn,恰为 1 到 n 的一个排列,表示使用这种洗牌法,
第 i位变为原来的 Xi位的牌。输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,且对每种
洗牌法,都存在一种洗牌法使得能回到原状态。

Output

不同染法除以P的余数

Sample Input

1 1 1 2 7
2 3 1
3 1 2

Sample Output

2

HINT

有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 和GRB。

100%数据满足 Max{Sr,Sb,Sg}<=20。

Source

[ Submit][ Status][ Discuss]

HOME   Back

题意 : 给定n个扑克牌,及有m中方式变换之后会重复,求满足r有sr种,b有sb种,g有sg种的不同的染色方法数.

很明显的polya计数.

具体的方法和证明方法请参见陈瑜希2008国家集训队论文.

论文下载地址 : 陈瑜希2008国家集训队论文-polya计数

下面是已经证明了Burnside引理后的做法:

首先n种洗牌方式就是n个置换,接着对于每个置换求循环节数及每个循环节的长度

至于每个循环节内是什么论文中已证明并不影响答案.

dp的正确性基于Burnside引理,置换中每一个循环只能染一种颜色

接着用一个4维DP(可以优化到3维..但是我没高兴-.-)

F[i][j][k][l]表示前i个置换,染了j种red,k种blue,l种green的方案总数

显然由于Burnside引理,对于每个置换dp方程为:

f[i][j][k][l] = f[i-1][j-s[i]][k][l] + f[i-1][j][k-s[i]][l] + f[i-1][j][k][l-s[i]];

s[j]表示第i个循环,第j个循环节的长度

最后的ans为每个置换dp终值的和 / 置换总数.

同时有一点要注意,要建立一个i->i的置换,作为第m+1个置换.

所以答案 = (ans / (m+1)) % p;

所以要求乘法逆元,但是由于p一定是质数,根据zzh理论(其实是费马小定理)

可以得到(ans / (m+1)) % p == (ans * (m+1)^(p-2))..就避免了求乘法逆元..


code:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <set>
#define PUSH push_back
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define REP(i, l, r) for (int i = l; i >= r; i--)
#define nil -1
#define oo 715827882
#define X first
#define Y second
#define N 110
#define M 30
  
using namespace std;
  
long long
    sr, sb, sg, n, m, p, a[N], s[N], f[N][M][M][M], total, S, K, len, x, y, d;
bool
    t[N][N], vis[N], bo;
long long
    ans = 0;
  
long long max(long long a, long long b)
{
    return (a>b ? a : b);
}
  
long long min(long long a, long long b)
{
    return (a<b ? a : b);
}
  
void dfs(long long k, long long s)
{
    vis[k] = 1;
    rep(i, 1, n)
    {
        if (bo)
            return;
        if ((t[k][i]) && ((!vis[i])||(i == S)))
        {
            if (i == S)
            {
                K = s;
                bo = 1;
                return;
            }
            dfs(i, s+1);
        }
    }
}
  
void DP()
{
    memset(f, 0, sizeof(f));
    f[0][0][0][0] = 1;
    rep(i, 1, len)
        rep(j, 0, sr)
            rep(k, 0, sb)
                rep(l, 0, sg)
                {
                    if (j >= s[i])
                        f[i][j][k][l] += f[i-1][j-s[i]][k][l];
                    if (k >= s[i])
                        f[i][j][k][l] += f[i-1][j][k-s[i]][l];
                    if (l >= s[i])
                        f[i][j][k][l] += f[i-1][j][k][l-s[i]];
                    f[i][j][k][l] %= p;
                }
    ans += f[len][sr][sb][sg] % p;
}
  
void extend_gcd(long long a,long long b,long long &d,long long &x,long long &y)
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        extend_gcd(b,a % b,d,y,x);
        y -= x * (a / b);
    }
}
  
long long power(long long x, long long y)
{
    long long
        s = 1;
    rep(i, 1, y)
    {
        s *= x;
        s %= p;
    }
    return s;
}
  
int main()
{
    scanf("%d%d%d%d%d", &sr, &sb, &sg, &m, &p);
    n = sr + sb + sg;
    rep(_, 0, m)
    {
        memset(t, 0, sizeof(t));
        memset(vis, 0, sizeof(vis));
        if (_ > 0)
            rep(i, 1, n)
            {
                scanf("%d", &a[i]);
                t[i][a[i]] = 1;
            }
        else
            rep(i, 1, n)
            {
                a[i] = i;
                t[i][i] = 1;
            }
        total = 0;
        len = 0;
        while (total < n)
        {
            rep(i, 1, n)
                if (!vis[i])
                {
                    S = i;
                    K = 0;
                    bo = 0;
                    dfs(i, 1);
                    total += K;
                    break;
                }
            s[++len] = K;
        }
        DP();
    }
    m++;
    ans *= power(m, p-2);
    ans %= p;
//  extend_gcd(m,p,d,x,y);
//  ans = (ans * ((x + p) % p)) % p;
    cout << ans << endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值