![](http://www.lydsy.com/JudgeOnline/image/logo.png)
F.A.Qs | Home | Discuss | ProblemSet | Status | Ranklist | Contest | ModifyUser Manacher | Logout | 捐赠本站 |
---|
1004: [HNOI2008]Cards
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
2 3 1
3 1 2
Sample Output
HINT
有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 和GRB。
100%数据满足 Max{Sr,Sb,Sg}<=20。
Source
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;
}