2019牛客暑期多校训练(第八场)J-Just Jump
题意
某人要从1走到L点,中间有L-1个点可以走,他每次最少走d步。有m个条件,在第ti步不能走到pi点。问有多少种走法。
思路
先dp求出没有条件时的方案数。然后容斥一下走到条件位置的方案数(减去走到一个条件位置的方案数,加上走到两个条件位置的方案数……)。
坑点
所有数组需要用int类型,在乘法或者三个数相加时要先转为LL类型。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e7+10;
const int MOD=998244353;
const int N=3005;
struct S
{
ll t,p;
} pos[N];
int f[maxn],nf[maxn];
int pow_(int x,int y)
{
int ret=1;
while (y)
{
if (y&1)
ret=1ll*ret*x%MOD;
x=1ll*x*x%MOD;
y>>=1;
}
return ret;
}
void init()
{
f[0]=nf[0]=1;
for (int i=1; i<maxn; i++)
f[i]=1ll*f[i-1]*i%MOD;
nf[maxn-1]=pow_(f[maxn-1],MOD-2);
for (int i=maxn-1; i; i--)
nf[i-1]=1ll*nf[i]*i%MOD;
}
int C(int x,int y)
{
return 1ll*f[x]*nf[y]%MOD*nf[x-y]%MOD;
}
bool cmp(S aa,S bb)
{
return aa.p<bb.p;
}
int dp[maxn],pre[maxn];
ll tmp[N];
int main()
{
init();
int l,d,m;
ll ans=0;
scanf("%d%d%d",&l,&d,&m);
for(int i=1; i<=m; i++)
{
scanf("%lld%lld",&pos[i].t,&pos[i].p);
}
sort(pos+1,pos+1+m,cmp);
pre[0]=1;
for(int i=1; i<d; i++)
{
pre[i]=pre[i-1];
}
for(int i=d; i<=l; i++)
{
dp[i]=pre[i-d];
pre[i]=(pre[i-1]+dp[i])%MOD;
}
tmp[0]=1;
for(int i=1; i<=m; i++)
{
for(int j=0; j<i; j++)
{
if(pos[i].t<pos[j].t||pos[j].p==pos[i].p)
continue;
if(1ll*d*(pos[i].t-pos[j].t)-(pos[i].p-pos[j].p)>0)
continue;
tmp[i]=(tmp[i]+1ll*tmp[j]*C(((pos[i].p-pos[j].p)-((pos[i].t-pos[j].t))*(d-1)-1),((pos[i].t-pos[j].t-1)))%MOD)%MOD;
}
tmp[i]=(MOD-tmp[i])%MOD;
}
for(int i=0; i<=m; i++)
{
ans=(ans+1ll*tmp[i]*dp[l-pos[i].p]%MOD)%MOD;
}
printf("%lld\n",ans);
return 0;
}