AGC021E Ball Eat Chameleons

Link
一只变色龙变成红色的要么是吃的红球比蓝球多,要么是吃的红球和蓝球一样多且最后一吃的是蓝球。
设总共有(R)个红球和(B)个蓝球,若(R<B)则显然无解;若(R=B)则最后一个球一定是蓝球,其方案数等于(R)个红球与(B-1)个蓝球时的方案数。因此我们只需要考虑(R>B)的情况。
(R-Bge n),那么我们可以让所有变色龙吃的红球都比蓝球多,因此任何一个序列都是合法的,方案数为({R+Bchoose R})
(R-B<n),那么(n−R+B)只变色龙吃的红球和蓝球一样多,(R−B)只变色龙吃的红球比蓝球多一个(若存在合法方案,那么一定存在一个上述形式的合法方案,可以用调整法证明)。
让吃的红球和蓝球一样多的变色龙都只吃一个红球和一个蓝球,剩下来的球不管以什么顺序吃都是合法的了。不难发现这是最优的构造方案。
也就是说序列合法的充要条件是能够取出(n-R+B)对不交的RB子序列。
这等价于对任意一个前缀而言,蓝球比红球多不超过(R-n)个。
转化为格路计数问题即可,方案数为({R+Bchoose R}-{R+Bchoose 2R-n+1})

#include<cstdio>
const int N=500007,P=998244353;
int fac[N],ifac[N];
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int mod(int x){return x+(x>>31&P);}
int pow(int a,int b){int r=1;for(;b;b>>=1,a=1ll*a*a%P)if(b&1)r=1ll*a*r%P;return r;}
int C(int n,int m){return n<m||m<0? 0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
int main()
{
    int n,k,ans=0;scanf("%d%d",&n,&k);
    for(int i=fac[0]=1;i<=k;++i) fac[i]=1ll*fac[i-1]*i%P;
    ifac[k]=pow(fac[k],P-2);for(int i=k;i;--i) ifac[i-1]=1ll*ifac[i]*i%P;
    for(int i=0,f;i+i<=k&&i+n<=k;++i) f=(i+i==k),inc(ans,mod(C(k-f,i-f)-C(k-f,i+i+n-k-1-f)));
    printf("%d",ans);
} 
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12755355.html