您的位置:首页 > 其它

BZOJ1004: [HNOI2008]Cards

2018-01-19 21:58 197 查看

1004: [HNOI2008]Cards

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3938 Solved: 2377
[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

【题解】

等价于给定一个置换集F(需要自己加入(1,2,3,4,...,n)),用三种颜色且每种颜色指定个数进行染色的方案数

使用Burnside定理,求不动点个数

不难发现,不动点就是循环节为1的循环,求出每个置换f的循环后,在循环上染色,使得每个独立的循环颜色相等,能够独立出来变成循环节为1的循环

这个可以用多重背包做,不需要任何优化,体积就是原循环节大小,往三个背包(三种颜色)里装,装满的方案数

做完之后,把所有不动点个数相加,除以置换数m + 1即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define abs(a) ((a) < 0 ? (-1 * (a)) : (a))
inline void swap(int &a, int &b)
{
long long tmp = a;a = b;b = tmp;
}
inline void read(int &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
}

const int INF = 0x3f3f3f3f;
const int MAXN = 60;

int dp[MAXN + 5][MAXN + 5][MAXN + 5], size[MAXN], tot, s[MAXN], b[MAXN];
int sr,sb,sg,m,p,n,sum;

int pow(int a, int b)
{
int r = 1, base = a;
for(;b;b >>= 1)
{
if(b & 1) r *= base, r %= p;
base *= base, base %= p;
}
return r;
}

int ni(int x)
{
return pow(x, p - 2);
}

int main()
{
read(sr), read(sb), read(sg), read(m), read(p);
n = sr + sb + sg;
for(register int i = 1;i <= m + 1;++ i)
{
tot = 0;memset(dp, 0, sizeof(dp));memset(size, 0, sizeof(size));memset(b, 0, sizeof(b));
if(i == m + 1) for(register int j = 1;j <= n;++ j) s[j] = j;
else for(register int j = 1;j <= n;++ j) read(s[j]);
for(register int j = 1;j <= n;++ j)
if(!b[j])
{
++ tot;
int k = j;
do
{
b[k] = 1;
++ size[tot];
k = s[k];
}while(k != j);
}
dp[0][0][0] = 1;
for(register int j = 1;j <= tot;++ j)
for(register int r = sr;r >= 0;-- r)
for(register int b = sb;b >= 0;-- b)
for(register int g = sg;g >= 0;-- g)
{
int &tmp = dp[r][b][g];
if(g >= size[j]) tmp += dp[r][b][g - size[j]];if(tmp >= p) tmp -= p;
if(b >= size[j]) tmp += dp[r][b - size[j]][g];if(tmp >= p) tmp -= p;
if(r >= size[j]) tmp += dp[r - size[j]][b][g];if(tmp >= p) tmp -= p;
}
sum += dp[sr][sb][sg];
if(sum >= p) sum -= p;
}
printf("%d\n", sum * ni(m + 1) % p);
return 0;
}


BZOJ1004
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: