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;
}

 

posted @ 2018-12-11 16:43  LLTYYC  阅读(187)  评论(0编辑  收藏  举报