[HNOI2008]Cards

HNOI2008 Cards

标签:数学 置换


题目描述

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

输入输出格式

输入格式:
第一行输入 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种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。
输出格式:
不同染法除以P的余数

输入输出样例

输入样例:
1 1 1 2 7
2 3 1
3 1 2
输出样例:
2

题解

对于这道题就不能用polya定理了(所以我就不知道怎么做了…)因为存在颜色数量的限制,不过对于题目这句话:数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。
因此它是满足置换群的性质的,因此我们可以用Burnside引理嘛,那么如何找不动点呢?其实就是每个循环节染相同颜色那么它肯定是一个不动点撒(由Burnside引理推polya定理也可以这么来推)因此我们可以dfs找出每种洗牌法的循环节及其长度,然后对于每种循环节染相同颜色。于是问题转换成把一些物品放到一些背包,直接DP即可了。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define N 100
#define ll long long
using namespace std;

int sr, sb, sg, p, n, m, cnt = 0;
int to[N], g[N];
ll f[N][N][N], ans = 0;
bool vis[N];

ll calc()
{
    memset(vis, false, sizeof(vis));
    memset(g, 0, sizeof(g));
    memset(f, 0, sizeof(f));
    cnt = 0; f[0][0][0] = 1;
    for(int i = 1; i <= n; i ++)
        if(!vis[i])
        {
            int x = i; cnt ++;
            while(!vis[x]) vis[x] = true, g[cnt] ++, x = to[x];
        }
    for(int i = 1; i <= cnt; i ++)
        for(int j = sr; j >= 0; j --)
            for(int k = sb; k >= 0; k --)
                for(int l = sg; l >= 0; l --)
                {
                    if(j >= g[i]) f[j][k][l] += f[j - g[i]][k][l], f[j][k][l] %= p;
                    if(k >= g[i]) f[j][k][l] += f[j][k - g[i]][l], f[j][k][l] %= p;
                    if(l >= g[i]) f[j][k][l] += f[j][k][l - g[i]], f[j][k][l] %= p;
                }
    return f[sr][sb][sg] % p;            
}

ll pow_mul(ll a, ll b, ll p)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return ans;
}

int main()
{
    scanf("%d%d%d%d%d", &sr, &sb, &sg, &m, &p);
    n = sr + sb + sg;
    for(int i = 1; i <= n; i ++) to[i] = i;
    ans = (ans + calc()) % p;
    for(int i = 1; i <= m; i ++)
    {
        for(int j = 1; j <= n; j ++) scanf("%d", &to[j]);
        ans = (ans + calc()) % p;
    }
    ans = ans * pow_mul(m + 1, p - 2, p) % p;
    printf("%lld", ans);
    return 0;
}

心得

区分polya定理和Burnsi适用范围,不过二者基本上都要找到循环节,所以如何优秀的找出循环节是解决这一类问题的关键。


  转载请注明: iSecloud's Blog [HNOI2008]Cards

 上一篇
[SHOI2006]有色图 [SHOI2006]有色图
SHOI2006 有色图标签: 数学 置换 polya定理 题目描述如果一张无向完全图(完全图就是任意两个不同的顶点之间有且仅有一条边相连)的每条边都被染成了一种颜色,我们就称这种图为有色图。如果两张有色图有相同数量的顶点,而且经过某种顶
2019-02-22
下一篇 
poj1286--Necklace of Beads poj1286--Necklace of Beads
poj1286–Necklace of Beads标签:数学 置换 polya定理 题目描述给出三种颜色红绿蓝,对一串n(n<24)个小球的环染色,环可以旋转和翻转,问最终可能有多少不同的染色方案。 输入输出格式InputThe i
2019-02-22