2019牛客多校第八场J.Just Jump(容斥原理)

2019牛客多校J.Just Jump(容斥原理)

题目大意

有一条河,长度为L,每次至少需要跳d个单位长,有m次攻击 &lt; t , p &gt; &lt;t,p&gt; <t,p>意味着时刻t攻击位置p(即此时不能跳到这个位置上)问从0跳到L有多少种跳法

解题思路

假设没有攻击的情况下,跳跃长度k可以通过递推得出
{ d p [ k ] = 0 k &lt; d d p [ k ] = ∑ i = 0 k − d d p [ i ] k &gt; = d \begin{cases} dp[k]=0&amp;k&lt;d\\ dp[k]=\sum_{i=0}^{k-d}dp[i]&amp;k&gt;=d \end{cases} {dp[k]=0dp[k]=i=0kddp[i]k<dk>=d
这可以通过前缀和方便地求出

接下来需要减去有攻击的情况这里我们只需要做容斥,加上至少一次进攻的方案数,减去至少两次的方案数…

而每种的方案数可以通过 C p − d t + t − 1 t − 1 C_{p-dt+t-1}^{t-1} Cpdt+t1t1乘上上一次攻击的答案再做累加得到(这里p,t的意为上次进攻和这次进攻的位置距离和时间距离)

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL ;
const int mod=998244353;
const int size=1e7+5;
int L,d,m;
int dp[size];
int sub[2][3005];
int fac[size],invfac[size];
int sum[size];
struct att
{
	int t,p;
	bool friend operator<(att x,att y)
	{
		return x.t<y.t;
	}
}at[3005];
int quick_pow(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=1LL*ans*a%mod;
		b>>=1;
		a=1LL*a*a%mod;
	}
	return ans;
}
void init()
{
	fac[0]=1;
	for(int i=1;i<size;i++) fac[i]=1LL*fac[i-1]*i%mod;
	invfac[size-1]=quick_pow(fac[size-1],mod-2);
	for(int i=size-2;i>=1;i--) invfac[i]=1LL*invfac[i+1]*(i+1)%mod;
}
int combi(LL a,LL b)
{
	if(a==0) return 1;
	if(a<0) return 0;
	return 1LL*fac[b]*invfac[b-a]%mod*invfac[a]%mod;
}
LL calc(LL k,LL l)
{
	l-=(LL)d*k;
	if(l<0) return 0;
	if(k==0) return l==0;
	return combi(k-1,l+k-1);
}
int main()
{
	scanf("%d%d%d",&L,&d,&m);
	dp[0]=sum[0]=1;
	init();
	for(int i=1;i<=L;i++)
	{
		if(i>=d) dp[i]=sum[i-d];else dp[i]=0;
		sum[i]=(sum[i-1]+dp[i])%mod;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&at[i].t,&at[i].p);
	}
	at[0].t=at[0].p=0;
	sort(at+1,at+1+m);
	sub[0][0]=1;
	LL ans=dp[L];
	for(int i=1;i<=m;i++)
	{
		for(int k=0;k<2;k++)
		{
			for(int j=0;j<i;j++)
			{
				sub[k][i]=(sub[k][i]+1LL*sub[k^1][j]*calc(at[i].t-at[j].t,at[i].p-at[j].p))%mod;
			}
		} 
		ans=(ans+1LL*(sub[0][i]-sub[1][i]+mod)*dp[L-at[i].p]%mod)%mod;
	}
	printf("%lld\n",ans);
}	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值