【组合数学】[HNOI2008][HYSBZ/BZOJ1004]Cards
网上的题解都说用置换,然而我不会,所以,我按照自己的方法写了一份题解。
题目链接
分析
对题目的分析
题目说
可以使用多种洗牌法,而每种方法可以使用多次然而
输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替
说明我们只用考虑仅使用一种洗牌法的情况即可。
然后怎么做呢?搜索?No!
首先,我们考虑一共有多少种染色的方法。
染色时,我们从n(
由于
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法洗成另一种
有m种洗牌法,任何一种染色方案都可以根据洗牌法变成其他m种方案,假设这种方案为A。
我们把这m+1中方案看做一组,这一组中的方案通过洗牌能且只能变成其他m种方案,且不会有不属于这一组的方案能通过洗牌变成这一组方案的任何一种。
Why?
对每种洗牌法,都存在一种洗牌法使得能回到原状态。
- 对于这一组的任何方案,都可以通过洗牌变为A,再变为其他方案,由于输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,所以这一组的方案能通过洗牌变成这一组方案的任何一种。
- 对于这一组的任何一组方案T,如果能够变为不属于这一组的方案B,那B->T->A,那么B->A,A->B(->是变成的意思),和假设B不属于这一组矛盾。
- 如果有不属于这一组的方案B能够变为这m+1种方案之一,证明方法同上。
所以,我们的染色方案被重复计算了m+1次。
One more thing
求解组合数的过程中,可以用longlong存储。
求解S时就不得不mod p了
但是最后一步有除法,怎么办?
乘m+1的逆元就好了,因为p>m+1,且p是质数,所以gcd(m+1,p)=1,由费马小定理可得
代码
#include<cstdio>
int sr,sg,sb,m,p,ans=1,n;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
int quick_pow(int a,int b){
int ret=1;
while(b){
if(b&1)
ret=ret*a%p;
a=a*a%p;
b>>=1;
}
return ret;
}
int main()
{
Read(sr),Read(sb),Read(sg),Read(m),Read(p);
n=sr+sb+sg;
int i;
long long t;
for(t=i=1;i<=sr;i++)
t=t*(n-i+1)/i;
ans=ans*(t%p)%p;
n-=sr;
for(t=i=1;i<=sg;i++)
t=t*(n-i+1)/i;
ans=ans*(t%p)%p;
ans=(ans*quick_pow(m+1,p-2))%p;
printf("%d",ans);
}
如果你还想看置换的做法,看看【Burnside定理/置换】[HNOI2008][HYSBZ/BZOJ1004]Cards.