P1446 [HNOI2008]Cards

$ \color{#0066ff}{ 题目描述 }$

小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.

进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.

Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

\(\color{#0066ff}{输入格式}\)

第一行输入 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种洗牌法中的一种代替,且对每种

洗牌法,都存在一种洗牌法使得能回到原状态。

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

\(\color{#0066ff}{输出格式}\)

不同染法除以P的余数

\(\color{#0066ff}{输入样例}\)

1 1 1 2 7
2 3 1
3 1 2

\(\color{#0066ff}{输出样例}\)

2

\(\color{#0066ff}{数据范围与提示}\)

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

[HNOI2008]

\(\color{#0066ff}{题解}\)

因为有颜色数量的限制,所以Polya就不太可行了

没事,我们还有Burnside

于是我们要找每个置换下不变的方案数

不难发现,对于每个循环,循环中的颜色必须相同才行

于是把当前置换的循环处理出来,当成物品跑01背包,就可以求出不变的方案数

注意还有额外的一种置换是不洗牌,也就是说不变,要特殊处理

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
int f[50][50][50];
bool vis[100];
int mp[100];
int siz[100];
int ans;
int a, b, c, m, mod, n;
LL ksm(LL x, LL y) {
	LL re = 1LL;
	while(y) {
		if(y & 1) re = re * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return re;
}
int work(int flag) {
	memset(f, 0, sizeof f);
	for(int i = 1; i <= n; i++) vis[i] = 0, siz[i] = 0;
	int num = 0;
	if(!flag) {
		for(int i = 1; i <= n; i++) {
			if(vis[i]) continue;
			num++;
			int now = i;
			while(!vis[now]) vis[now] = true, siz[num]++, now = mp[now];
		}
	}
	else {
		num = n;
		for(int i = 1; i <= n; i++) siz[i] = 1;
	}
	f[0][0][0] = 1;
	for(int i = 1; i <= num; i++)
		for(int A = a; A >= 0; A--)
			for(int B = b; B >= 0; B--)
				for(int C = c; C >= 0; C--) {
					if(A >= siz[i]) (f[A][B][C] += f[A - siz[i]][B][C]) %= mod;
					if(B >= siz[i]) (f[A][B][C] += f[A][B - siz[i]][C]) %= mod;
					if(C >= siz[i]) (f[A][B][C] += f[A][B][C - siz[i]]) %= mod;
				}
	return f[a][b][c];
}

int main() {
	n = (a = in()) + (b = in()) + (c = in());
	m = in(), mod = in();
	for(int i = 1; i <= m; i++) {
		for(int j = 1; j <= n; j++) mp[j] = in();
		(ans += work(0)) %= mod;
	}
	(ans += work(1)) %= mod;
	printf("%lld", 1LL * ans * ksm(m + 1, mod - 2) % mod);
	return 0;
}
posted @ 2019-02-24 17:32  olinr  阅读(148)  评论(0编辑  收藏  举报