题目链接: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个攻击点,我们要减去攻击次数为奇数的方案,加上攻击次数为偶数的方案(容斥),对于每一次攻击事件它的方案数为。我们可以定义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);
}
我什么时候才能补得比队友快呢 @ @?