题目链接:https://ac.nowcoder.com/acm/contest/888/J
题意:有一只青蛙在0位置处,他想跳到L位置处,但是路上会受到一些攻击,这只青蛙在出发前就知道自己在跳第几次的时间时哪一个位置会受到攻击,并且青蛙每次跳的距离要大于等于d,问青蛙安全(不受一次攻击)跳到L处的方案数
数据范围:1≤d≤L≤1e7,1≤t,p<L,1≤m≤3000,mod=998244353;
思路:首先不考虑受攻击的情况,那么可以用一个dp和前缀和算出所有情况dp[i]=sum[i-d],sum[i]=sum[i-1]+dp[i].然后考虑受到一次攻击的情况就是对于每个受攻击的位置,正好第ti步跳到那里受到攻击那么情况数,那么只需要容斥一下,减去一次受到攻击的次数,加上两次受到攻击的次数,减去三次受到攻击的次数。。。。。这个可以通过对pi排序暴力容斥一下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e7+10;
const int mod=998244353;
int n,m,t,L,d,b[N],dp[N],sum[N],fac[N],facv[N];
pair<int,int>a[3005];
int quickpow(ll a,ll b){
int ans=1;
a%=mod;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans%mod;
}
void init(){
fac[0]=facv[0]=1;
for(int i=1;i<N;i++)fac[i]=1ll*fac[i-1]*i%mod;
facv[N-1]=quickpow(fac[N-1],mod-2);
for (int i=N-1;i;i--)facv[i-1]=1ll*facv[i]*i%mod;
}
int C(int x,int y){
if(x<y) return 0;
return 1ll*fac[x]*facv[y]%mod*facv[x-y]%mod;
}
int solve(int l,int r){
if(a[l].second>=a[r].second||a[l].first==a[r].first)return 0;
int len=a[r].first-a[l].first,tm=a[r].second-a[l].second;
if (1ll*tm*d-len>0) return 0;
return C(len-tm*d+tm-1,tm-1);
}
int main(){
init();
scanf("%d%d%d",&L,&d,&m);
for(int i=1;i<=m;i++)scanf("%d%d",&a[i].second,&a[i].first);
sort(a+1,a+m+1);
sum[0]=b[0]=1;
for(int i=1;i<d;i++)sum[i]=sum[i-1];
for(int i=d;i<=L;i++){
dp[i]=sum[i-d];
sum[i]=(sum[i-1]+dp[i])%mod;
}
for(int i=1;i<=m;i++){
for(int j=0;j<i;j++)b[i]=(b[i]+1ll*b[j]*solve(j,i)%mod)%mod;
b[i]=(mod-b[i])%mod;
}
for(int i=1;i<=m;i++)dp[L]=(dp[L]+1ll*b[i]*dp[L-a[i].first]%mod)%mod;
printf("%d\n",dp[L]);
return 0;
}