810975(2021 CCPC 威海 M)

传送门

题目大意

你玩了n把游戏,其中赢了m把,然后最长的连赢长度为k,问有多少种情况。(0n,m,k105)

思路

这道题一开始想的时候还以为是一道普普通通的排列组合题,但是发现并不能很好的分析清楚。看了Solemntee大佬的题解后才发现原来可以用容斥做。我们可以设ansk为最长连赢次数大于等于k的情况,然后我们所要求的答案就是anskansk+1。然后我们来分析一下ansk怎么求。我们首先可以先把赢的情况挖空,然后有i次连赢次数大于等于k的情况就是(nm+1i),然后我们剩下的位置就可以随便放了,情况就是(niknm),于是ansk的结果就是i=1ik<=m(1)i+1(nm+1i)(niknm),然后差不多就结束了,不过还得特判一下k==0的情况,然后就可以AC啦!

代码

#include<bits/stdc++.h>
using namespace std;
long long mod=998244353;
int MAX=100005;
vector<long long>fac,inv,finv;
void binom_init()
{
    fac.resize(MAX);
    finv.resize(MAX);
    inv.resize(MAX);
    fac[0] = fac[1] = 1;
    inv[1] = 1;
    finv[0] = finv[1] = 1;
    for (int i = 2; i < MAX; i++)
    {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = mod - mod / i * inv[mod % i] % mod;
        finv[i] = finv[i - 1] * inv[i] % mod;
    }
}
long long binom(long long n, long long r)
{
    if (n < r || n < 0 || r < 0) return 0;
    return fac[n] * finv[r] % mod * finv[n - r] % mod;
}
int main()
{
    binom_init();
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    if(k==0)
    {
        printf("%d\n",m==0);
        return 0;
    }
    long long ans1=0,ans2=0;
    for(long long i=1;i*k<=m;i++)
    {
        if(i&1)ans1=(ans1+binom(n-m+1,i)%mod*binom(n-i*k,n-m)%mod+mod)%mod;
        else ans1=(ans1-binom(n-m+1,i)%mod*binom(n-i*k,n-m)%mod+mod)%mod;
    }
    for(long long i=1;i*(k+1)<=m;i++)
    {
        if(i&1)ans2=(ans2+binom(n-m+1,i)%mod*binom(n-i*(k+1),n-m)%mod+mod)%mod;
        else ans2=(ans2-binom(n-m+1,i)%mod*binom(n-i*(k+1),n-m)%mod+mod)%mod;
    }
    printf("%lld\n",(ans1-ans2+mod)%mod);
    return 0;
}

__EOF__

本文作者Jerry-Black
本文链接https://www.cnblogs.com/Jerry-Black/p/15969367.html
关于博主:小蒟蒻一只( ̄^ ̄)ゞ
版权声明:转载请注明来源哟~ QAQ
声援博主:UP UP UP !!!
posted @   Jerry_Black  阅读(574)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示