1004: [HNOI2008]Cards
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3605 Solved: 2155
[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
【分析】
辣鸡置换学不会
burnside引理
分别计算每一个置换下的循环节个数,那么循环节各部分相互独立,循环节内部只能染一个颜色= =
啪啪啪一顿背包
现在才学burnside…NOI药丸啊
【代码】
//bzoj 1004 Cards
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=65;
bool vis[mxn];
int n,m,p,a,b,c,ans;
int A[mxn],f[mxn],sum[mxn],dp[mxn][25][25];
inline int ksm(int x,int k)
{
if(k==0) return 1;
if(k==1) return x%p;
int tmp=ksm(x,k>>1);
if(k&1) return tmp*tmp*x%p;
else return tmp*tmp%p;
}
inline int dfs(int now,int cnt)
{
if(vis[now]) return cnt;
vis[now]=1,dfs(A[now],cnt+1);
}
inline int DP()
{
int i,j,k,tot=0;
memset(vis,0,sizeof vis);
memset(dp,0,sizeof dp);
fo(i,1,n)
if(!vis[A[i]])
f[++tot]=dfs(A[i],0);
fo(i,1,tot) sum[i]=sum[i-1]+f[i];
dp[0][0][0]=1;
fo(i,1,tot)
fo(j,0,a)
fo(k,0,b)
{
if(j>=f[i]) dp[i][j][k]+=dp[i-1][j-f[i]][k];
if(k>=f[i]) dp[i][j][k]+=dp[i-1][j][k-f[i]];
if(sum[i]-j-k>=f[i]) dp[i][j][k]+=dp[i-1][j][k];
dp[i][j][k]%=p;
}
return dp[tot][a][b];
}
int main()
{
int i,j,k;
scanf("%d%d%d%d%d",&a,&b,&c,&m,&p);
n=a+b+c;
fo(i,1,m)
{
fo(j,1,n) scanf("%d",&A[j]);
ans=(ans+DP())%p;
}
fo(i,1,n) A[i]=i;
ans=(ans+DP())%p;
printf("%d\n",ans*ksm(m+1,p-2)%p);
return 0;
}