[HNOI2008]Cards

[HNOI2008]Cards

Time Limit:10000MS  Memory Limit:165536K
Total Submit:114 Accepted:65
Case Time Limit:1000MS

Description

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

第一行输入五个整数:Sr,Sb,Sg,M,P(M<=60,M+1

<100).
N=Sr+Sb+Sg.接下来M行,每行描述一种洗牌法,每行有N个用空格隔开的整数X1X2…Xn.
恰为1到N的一个排列,表示使用这种洗牌法,第I位变成原来的Xi位的牌.
输入数据保证任意多次洗牌都可以用M种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到
原状态.
20%的数据保证Sr+Sb+Sg<=20
100%的数据保证Max(Sr,Sb,Sg)<=20

Input

第一行输入五个整数:Sr,Sb,Sg,M,P(M<=60,M+1

<100).N=Sr+Sb+Sg.
接下来M行,每行描述一种洗牌法,每行有N个用空格隔开的整数X1X2…Xn.
恰为1到N的一个排列,表示使用这种洗牌法,第I位变成原来的Xi位的牌.
输入数据保证任意多次洗牌都可以用M种洗牌法中的一种代替,且对每种洗牌法
都存在一种洗牌法使得能回到原状态.
20%的数据保证Sr+Sb+Sg<=20
100%的数据保证Max(Sr,Sb,Sg)<=20

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。

Source
昨天很累啊,做了这题差不多就睡了。。今天才写这个,这题就是Polya计数法的扩展应用了。
本质上上是对每个置换的不动点数量求出平均值,不动点数量由于需要满足颜色的要求只能用Dp来搞,同时求出平均值需要除法,幸好P是素数,所以直接乘以拟元就可以了。。

#include <vector>
#include <algorithm>
#include <utility>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#define rep(i,n) for(int i=0;i<n;i++)
#define pb push_back
const int inf=~0U>>1,maxn=60;
typedef long long ll;
using namespace std;
int a,b,c,m,p,n;ll ret;
int M[maxn];
void Plus(int&a,int b)
{
a+=b;if(a>=p)a-=p;
}
int Cal(int A[maxn])
{
static int B[maxn],s;
static bool V[maxn];
static int Dp[2][maxn][maxn];
s=0;memset(V,0,sizeof(V));
rep(i,n)if(!V[i])
{
int t=0,x=i;
while(!V[x])V[x]=true,x=A[x],t++;
B[s++]=t;
}
int now=0,next=1;
memset(Dp,0,sizeof(Dp));Dp[next][0][0]=1;
rep(o,s)
{
swap(now,next);memset(Dp[next],0,sizeof(Dp[next]));
int t=B[o],tmp;
rep(i,a+1)rep(j,b+1)if(tmp=Dp[now][i][j])
{
Plus(Dp[next][i+t][j],tmp);
Plus(Dp[next][i][j+t],tmp);
Plus(Dp[next][i][j],tmp);
}
}
return Dp[next][a][b];
}
int pow_mod(int a,int b)
{
if(b==1)return a;
ll tmp=pow_mod(a,b/2);tmp*=tmp;tmp%=p;
if(b&1)tmp*=a,tmp%=p;
return tmp;
}
int main()
{
//freopen("in","r",stdin);
cin>>a>>b>>c>>m>>p;n=a+b+c;
rep(i,m)
{
rep(j,n)scanf("%d",M+j),M[j]–;
ret+=Cal(M);
}
rep(i,n)M[i]=i;ret+=Cal(M);
ret=ret*pow_mod(m+1,p-2);ret%=p;
cout<<ret<<endl;
}


6 thoughts on “[HNOI2008]Cards

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>