牛客多校第八场 Just Jump

本文介绍了一种结合容斥原理与动态规划(DP)的算法,用于解决一个跳跃游戏问题。在一个宽度为L的海域中,玩家从起点0出发,每次跳跃至少前进d距离,目标是在某些石头被攻击的情况下,计算安全到达终点L的方案数量。文章详细解释了如何使用DP计算总方案数,以及如何通过容斥原理减去受攻击的方案数。

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

牛客多校第八场 Just Jump(容斥+dp)

题意

有一片宽度为L的海,海里有L-1块石头,这些石头的位置分别是1,2,…,L-1,你的初始位置为0。你每个时刻都要往前前进至少d距离,并且你知道,在一些时刻,一些石头的位置会被攻击,要求计算安全到达L的方案数是多少。

数据范围:$1\le d \le L \le 1e7 ,,,m \le 3000$,m为攻击的次数,每次攻击被描述为两个数字:ti,pi,代表在第ti次跳跃之后,pi位置的石头会被攻击

题解

先不考虑攻击,则总方案数可以通过dp求出:dp[i]=∑dp[j],j+d≤idp[i]=\sum dp[j],j+d\le idp[i]=dp[j],j+di,用前缀和优化一下就能线性求出这个了。

然后考虑减去被攻击的方案,这个就要容斥了。首先我们可以将攻击按时间或位置排序,这样可以保证在受到某一次攻击之后,不会再受到这个攻击之前的攻击,这样会比较方便统计一点。然后用g[i][2]g[i][2]g[i][2]记录处在第i次攻击状态,并且前面经历了奇数次攻击和偶数次攻击的状态数,从一个攻击状态到另一个攻击状态可以用组合数计算,而这个攻击状态到L点的方案数就是dp[L−p[i]]dp[L-p[i]]dp[Lp[i]],最后减去奇数的方案数,加上偶数的方案数即可

代码

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<x<<" ";test(args...);}

typedef long long ll;
const ll mod = 998244353;
ll L,d,m;
struct node{
    ll t,p;
    bool operator<(const node & x)const{
        return t<x.t;
    }
}a[3009];
ll dp[10000009],sum[10000009];
ll g[3009][2];
ll jie[10000009],ni[10000009];
ll quick(ll a,ll x){
    if(x==1)return a;
    if(x==0)return 1;
    ll m=quick(a,x/2);
    m=m*m%mod;
    if(x&1)m=m*a%mod;
    return m;
}
ll Ni(ll x){
    if(ni[x])return ni[x];
    return ni[x]=quick(jie[x],mod-2);
}
ll C(ll m,ll n){
    return jie[m]*Ni(m-n)%mod*Ni(n)%mod;
}
int main() {
    scanf("%lld%lld%lld",&L,&d,&m);
    jie[0]=1;
    for(int i=1;i<=L;i++)jie[i]=jie[i-1]*i%mod;
    for(int i=1;i<=m;i++)scanf("%lld%lld",&a[i].t,&a[i].p);
    dp[0]=1;
    sum[0]=1;
    for(int i=1;i<=L;i++){
        if(i>=d)dp[i]=sum[i-d];
        sum[i]=(sum[i-1]+dp[i])%mod;
    }
    a[0].t=0,a[0].p=0;
    sort(a+1,a+1+m);
    g[0][0]=1;
    for(int i=1;i<=m;i++){
        for(int j=0;j<=1;j++){
            g[i][j]=0;
            for(int k=0;k<i;k++){
                if(a[k].p+(a[i].t-a[k].t)*d<=a[i].p)
                    g[i][j]=(g[i][j]+g[k][j^1]*C(a[i].p-a[k].p-(a[i].t-a[k].t)*(d-1)-1,(a[i].t-a[k].t)-1))%mod;
            }
        }
    }
    ll ans=0;
    for(int i=0;i<=m;i++){
        ans=(ans+g[i][0]*dp[L-a[i].p]%mod)%mod;
        ans=(ans-g[i][1]*dp[L-a[i].p]%mod+mod)%mod;
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值