BZOJ1004 Cards

来源:互联网 发布:知天命解释 编辑:程序博客网 时间:2024/06/01 20:34

网上一堆题解都是置换群+DP的,其实组合数学随便乱搞就可以了。

不考虑洗牌的情况,所有方案数为C(ra+rg+rb,ra)*C(rg+rb,rb)mod p

我们考虑每一种初始状态,通过m种洗牌法可以变为另外m种状态。

题目中有提到,“输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代

替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。“

我们假设第一次洗牌得到的一种情况x,假设x通过第二次洗牌可以得到y,且y不在第一次洗牌得到的结果集合中。那么y可以变回原状态x,x可以变回原状态,那么由y可以回到初始状态,与y不在集合中矛盾。

也就是说,对于一种状态,有m+1种状态和它打包在一起,是独立的。

那么最终的答案就是C(ra+rg+rb,ra)*C(rg+rb,rb)mod p除一下(m+1)了

注意要写个逆元

#include <bits/stdc++.h>using namespace std;void read(int &x){    char c;int f=1,ans=0;    c=getchar();    while (c!='-' && (c<'0' || c>'9'))    c=getchar();    while (c=='-')    {    f*=-1;    c=getchar();        }    while (c>='0' && c<='9')    {        ans=ans*10+c-'0';        c=getchar();    }    x=ans*f;    return ;}#define maxn (int)1e6#define ll long longll a[maxn];int p;ll qpow(ll x,ll m){    if (m==0) return 1;    ll tmp=qpow(x,m/2);    tmp=(tmp*tmp)%p;    if (m%2==1) tmp=(tmp*x)%p;    return tmp;}ll getc(ll n,ll m){    if (m>n) return 0;    return (a[n]*qpow(a[m],p-2))%p*qpow(a[n-m],p-2)%p;}ll lucas(long long n,long long m){    if (m==0) return 1;    return (getc(n%p,m%p)*lucas(n/p,m/p))%p;}int main(){    int n,m,aa,b,c;    read(aa);read(b);read(c);read(m);read(p);    ll t=1;    ll ans=1;    n=aa+b+c;    a[1]=1;    for (int i=2;i<=p;i++)        a[i]=(a[i-1]*i)%p;    ans=ans*lucas(n,aa)%p;    ans=ans*lucas(n-aa,b)%p;    ans=ans*qpow(m+1,p-2)%p;    cout<<ans<<endl;    return 0;}
其实自己置换学得很菜,学好了再回来补下置换解法的坑吧。

原创粉丝点击