(Nowcoder) J.Just Jump (动态规划+组合数+容斥)

探讨L长数轴上,从起点跳跃至终点的方案数计算,考虑到特定位置的跳跃限制,运用动态规划与容斥原理解决复杂路径计数问题。

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

传送门

题意:L长的数轴,从0开始跳,每次可以跳>=d的距离,但是有m限制,就是ti时刻不得跳到pi处,求到L的方案数

解:先不考虑m限制,dp很容易推出了,前缀和优化就是O(n)的。然后考虑限制减去,就是求出经过一次pi(限制处)再抵达L的方案数,这里我们很容易想到容斥定理,用容斥来求。官解:

解释一下这个组合数,就是先pi先减去d*ti的最低限制,剩下的就是多余的部分,但是ti次跳跃可能会有0(多余部分) ,下面就看作小球了,因为有0,所以就加入ti个球,现在就是要求小球放入ti个盒子非空了,然后再插入ti-1块隔板即可。

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int mod=998244353;
const int N=1e7+5,M=3e3+5;
ll dp[N],sum[N],b[M];
struct node {
    ll t,p;
}a[M];
bool cmp(node x,node y) {return x.p<y.p;}

ll l,d,m;
ll fac[N],inv[N];
ll fpow(ll x,ll y) {
    ll ans=1;
    while(y) {
        if(y&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
ll C(int n,int m) {
    if(m>n || n<0 || m<0) return 0;
    ll ans=1;
    ans=(ans*fac[n])%mod;
    ans=(ans*inv[m])%mod;
    ans=(ans*inv[n-m])%mod;
    return ans;
}
void init() {
	fac[0]=inv[0]=1;
    for(int i=1;i<=10000000;i++) fac[i]=(1LL*fac[i-1]*i)%mod;
    inv[10000000]=fpow(fac[10000000],mod-2);
    for(int i=9999999;i>=1;i--) inv[i]=(1LL*inv[i+1]*(i+1))%mod;
}
ll cal(ll p,ll t) {
    p-=t*d;
    if(p<0) return 0;
    else return C(p+t-1,t-1);
}
ll solve(){
	sort(a+1,a+1+m,cmp);
    for(int i=1;i<=m;i++) {
        b[i]=cal(a[i].p,a[i].t); 
        for(int j=1;j<i;j++) {
            if(a[j].t>a[i].t) continue;
            if(a[j].p==a[i].p) continue;
            b[i]=(b[i]-1LL*b[j]*cal(a[i].p-a[j].p,a[i].t-a[j].t)+mod)%mod;     
        }
    }
	ll res=0;
	for(int i=1;i<=m;++i)
		res=(res+b[i]*dp[l-a[i].p]%mod)%mod;
	return res;
}
int main() {
    init();
    scanf("%lld%lld%lld",&l,&d,&m);
    for(int i=1;i<=m;i++)    scanf("%lld%lld",&a[i].t,&a[i].p);
    dp[0]=sum[0]=1;
    for(int i=1;i<=l;++i){
		if(i<d) dp[i]=0,sum[i]=1;
		else dp[i]=sum[i-d],sum[i]=(sum[i-1]+dp[i])%mod;
	}
    printf("%lld\n",(dp[l]-solve()+mod)%mod);
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值