Description
小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有
多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方
案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗
成另一种.Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).
Input
第一行输入 5 个整数:Sr,Sb,Sg,m,p,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。
题解:
首先,考虑共有多少种染色的方法。
染色时,我们先从n张牌中选sr张染成红色,再从剩下的(n-sr)张中选sb张染成蓝色,剩下的染成绿色,根据乘法原理,
总方案数为t=C(n,sr)*C(n-sr,sb)*C(n-sr-sb,sg)=C(n,sr)*C(n-sr,sb)*1
在总方案数中,会有一些重复的需要筛去,接下来我们就需要计算所有染色方案中被重复计算了多少次。
共有m种洗牌法,任何一种染色方案(A)都可以根据洗牌法变成其它m种方案。我们把这m+1种方案看作一组,这一组中的方案通过洗牌能且只能变成其它m种方案,且不会有不属于这一组的方案能通过洗牌变成这一组方案的任何一种,固染色方案被我们重复计算了(m+1)次。
ans=(t/(m+1))%p;
最后一步,对于带除法的模运算,我们需要乘(m+1)的逆元。
因为p>m+1且p是质数(题目),所以gcd(m+1,p)=1;由费马小定理可得(m+1)^(p-1)≡1 mod p —> (m+1)^(p-2) * (m+1)≡1 mod p
固 (m+1)的逆元为(m+1)^(p-2).
代码如下:
#include<cstdio>
using namespace std;
int sr,sg,sb,m,p,n,xx,ans=1;
long long c;
long long quick(long long a,long long b,long long k)
{
long long sum=1;
while(b)
{
if(b&1) sum=(sum*a)%k;
b>>=1;
a=(a*a)%k;
}
return sum;
}
int main()
{
scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p);n=sr+sb+sg;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&xx);
c=1;
for(int k=1;k<=sr;k++)
{
c=c*(n-k+1)/k;
}
ans=ans*(c%p)%p;
n-=sr;c=1;
for(int k=1;k<=sg;k++)
c=c*(n-k+1)/k;
ans=ans*(c%p)%p;
ans=(ans*quick(m+1,p-2,p))%p;
printf("%d\n",ans);
}