2019牛客暑期多校训练营(第八场)J Just Jump(组合数学+容斥问题+dp)

本文探讨了一个计数问题,即一个人从起点走到终点,避开特定攻击点的方案数。通过预处理无攻击情况下的路径数,并结合容斥原理,文章详细解释了如何计算在存在攻击点的情况下,人物安全到达目的地的方案数量。使用C++实现算法,包括快速幂、组合数计算等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:https://ac.nowcoder.com/acm/contest/888/J

题意:有个人他要从0走到N,每次必须走至少d个单位长度,在途中,还有m次攻击事件,分别用ti,pi表示,当这个人的第ti 步是位置pi时,他就会遭到攻击,问这个人安全走完全程的方案有多少种。

1=<d,L<=1e7        1=<t,p<L       1=<m<=3000     答案对998244353取模

思路:我们可以先算出没有攻击事件时走到每一个位置的方案数同时取前缀和,剩下的事情就是减去会受到攻击的情况。我们先把m组t,p按照p从小到大排序(利用pair自动以第一维排序),然后我们遍历m个攻击点,我们要减去攻击次数为奇数的方案,加上攻击次数为偶数的方案(容斥),对于每一次攻击事件它的方案数为C\binom{ti-1}{pi-d*ti+ti-1}*dp[L-pi]。我们可以定义g[i][0]为受到偶数次攻击的组合数和,g[i][1]为受到奇数次攻击的组合数和,再把每个i和相应dp数相乘即可。

下面是AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=1e7+10;
const int P=998244353;
pair<int,int>pi[3005];
typedef long long LL;
int L,d,m;
LL dp[N],g[3005][2],f[N],invf[N];
LL quickpow(LL a,LL m)
{
    LL res=1;
    LL b=a;
    while(m)
    {
        if(m&1)
            res=res*b%P;
        b=b*b%P;
        m>>=1;
    }
    return res;
}
LL C(LL a,LL b)
{
    return (1ll*f[a]*invf[a-b]%P)*1ll*invf[b]%P;
}
int main()
{
    scanf("%d%d%d",&L,&d,&m);
    f[0]=invf[0]=1;
    for(int i=1; i<=L; i++)
        f[i]=f[i-1]*i%P;
    invf[L]=quickpow(f[L],P-2);
    for(int i=L-1; i>=1; i--)
        invf[i]=invf[i+1]*(i+1)%P;
    for(int i=1; i<=m; i++)
        scanf("%d%d",&pi[i].second,&pi[i].first);
    sort(pi+1,pi+m+1);
    dp[0]=1;
    for(int i=1; i<=L; i++)
    {
        if(i<d)
            dp[i]=0;
        else
        {
            dp[i]=dp[i-d];

        } dp[i]=(dp[i]+dp[i-1])%P;
    }
    LL ans=(dp[L]-dp[L-1]+P)%P;
    g[0][0]=1;
    long long subp,subt,q,qq,temp;
    for(int i=1; i<=m; i++)
    {
        for(int j=0; j<i; j++)
        {
            subp=pi[i].first-pi[j].first;
            subt=pi[i].second-pi[j].second;
            if(subp<subt*d||subt<=0)
                continue;
            q=subt-1,qq=subp-subt*d+q;
            temp=C(qq,q);
            g[i][0]=(g[i][0]+g[j][1]*temp)%P;
            g[i][1]=(g[i][1]+g[j][0]*temp)%P;
        }
        ans=(ans+(g[i][0]-g[i][1]+P)%P*(dp[L-pi[i].first]-dp[L-pi[i].first-1]+P)%P)%P;
    }
    printf("%lld\n",ans);

}

我什么时候才能补得比队友快呢  @ @?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值