【组合计数】ARC061F Card Game for Three

题意:

有三堆卡牌,牌数分别为N,M,K
每张牌有一个字母(’a’、’b’、’c’)表示下一个拿哪一堆。
现在要求第一堆首先拿完。求方案数。


分析:

首先,这道题有很多角度可以入手。但最简单的方法是,根据拿的牌的类型计算。
如果第一堆拿完,则拿的顺序中必然有n个a,且b的数量比m小,c的数量比k小。
但是最后一个必须限定为a,所以这部分拿了的方案数应为 Cn1n1+i C n − 1 + i n − 1 (i表示多拿的b和c的总和)
然后还要从这i个中选哪些是b,哪些是c,所以还要乘上 0x<m,0y<k[x+y=i]Cxi ∑ 0 ≤ x < m , 0 ≤ y < k [ x + y = i ] C i x
所以有如下式子:
Ans=i<m+ki=0Cn1n1+i3m+ki0x<m,0y<k[x+y=i]Cxi A n s = ∑ i = 0 i < m + k C n − 1 + i n − 1 3 m + k − i ∑ 0 ≤ x < m , 0 ≤ y < k [ x + y = i ] C i x
然后这就可以拿所谓的部分分了。

接下来一般来说都会想怎么改进式子,然而。。。这道题的方法是,直接枚举前半部分(即i的大小),想办法快速转移后半部分。
后半部分可以分三种情况讨论(设 km k ≤ m ):
注:下面x表示的是拿c的个数
1、 xix=0Cxi(i<k) ∑ x = 0 x ≤ i C i x ( i < k ) 即c的数量比i还多,所以任意拿就可以了
2、 x<kx=0Cxi(ki<m) ∑ x = 0 x < k C i x ( k ≤ i < m ) 即c的数量不超过i,这时c就不能拿超过k个
3、 x<kx=im+1Cxi(mi<m+k) ∑ x = i − m + 1 x < k C i x ( m ≤ i < m + k ) 即c的数量不超过i,b的数量也不超过i,这时c不能拿大于等于k个,b不能拿大于等于m个。

考虑优化转移。在第一种情况下,i变为i+1,只需要把总和乘2即可。
在第二种情况下,i变为i+1,把总和乘以2再减去 Cki C i k
在第三种情况下,i变为i+1,把总和乘以2再减去 Cki+Cmi C i k + C i m
用杨辉三角的性质来解释就很容易了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 900010
#define MOD 1000000007
using namespace std;
typedef long long ll;
ll inv[MAXN],fac[MAXN],pow3[MAXN];
ll fsp(ll x,int y){
    ll res=1;
    while(y){
        if(y&1)
            res=res*x%MOD;
        x=x*x%MOD;
        y>>=1;  
    }
    return res;
}
ll C(int x,int y){
    return fac[x]*inv[y]%MOD*inv[x-y]%MOD;
}
int main(){
    int n,m,k;
    SF("%d%d%d",&n,&m,&k);
    pow3[0]=1;
    fac[0]=1;
    for(int i=1;i<=n+m+k;i++){
        pow3[i]=pow3[i-1]*3ll%MOD;  
        fac[i]=fac[i-1]*i%MOD;
    }
    inv[n+m+k]=fsp(fac[n+m+k],MOD-2);   
    for(int i=n+m+k;i>0;i--)
        inv[i-1]=inv[i]*i%MOD;
    n--;
    if(m<k)
        swap(m,k);
    ll j=1,ans=0;
    for(int i=0;i<=m+k;i++){
        ans=(ans+C(n+i,n)*pow3[m+k-i]%MOD*j)%MOD;   
        if(i<k)
            j=j*2ll%MOD;
        else if(i<m)
            j=(j*2ll-C(i,k))%MOD;
        else    
            j=(j*2ll-C(i,k)-C(i,m))%MOD;
    }
    ans=(ans+MOD)%MOD;
    PF("%lld",ans);
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值