BZOJ 1004: [HNOI2008]Cards
注意题目的条件: "输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代
替"
所以对于每种方案,只要考虑经过一次洗牌后可能变成的情况
显然,如果有 m 种洗牌法,那么一种方案就可以被洗出 m+1 种方案
继续考虑,如果有另一种方案不属于这 m+1 种方案
那么它一定也可以洗出另外不重复的 m+1 种方案
所以推广一下,如果不考虑洗牌时方案有 sum 种,那么最后答案就是 $\frac{sum}{m+1}$
然后考虑如何求 sum
设 $n=S_r+S_b+S_g$
红色可以在 n 张空白牌中随便选,所以有 $C^{S_r}_{n}$ 种选法
然后考虑蓝色,因为红色已经选了,所以只能在 $n-S_r$ 中随便选,有 $C^{S_b}_{n-S_r}$ 种选法
最后绿色没得选了,只能把剩下的全染成绿色,有1种选法
乘起来,化简一波,得到$\frac{(S_r+S_b+S_g)!}{(S_r!\cdot S_b!\cdot S_g!)}$
最后答案再除以 $(m+1)$ 就好了
注意有取余,所以除要乘逆元
直接费马小定理就好了,代码就不注释了
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int sa,sb,sc,m,mo; inline int ksm(int x,int y) { int res=1; while(y) { if(y&1) res=res*x%mo; x=x*x%mo; y>>=1; } return res; } int f[107]; int main() { sa=read(); sb=read(); sc=read(); m=read(); mo=read(); int a; for(int i=1;i<=m;i++) for(int i=1;i<=sa+sb+sc;i++) a=read(); f[0]=1; for(int i=1;i<=sa+sb+sc;i++) f[i]=f[i-1]*i%mo; printf("%d",f[sa+sb+sc]*ksm(f[sa]*f[sb]%mo*f[sc]%mo*(m+1)%mo,mo-2)%mo); return 0; }