牛客第八场 J Just Jump —— 思维dp + 组合数

题目链接:点我啊╭(╯^╰)╮

题目大意:

     L L L 块石头,每次至少跳 d d d 步,从 0 0 0 跳到 L L L
     m m m 次攻击,在 t i t_i ti 这个时间不能在 p i p_i pi 位置上
    求所有的方案数???

解题思路:

    不考虑攻击
    设 f [ i ] f[i] f[i] 为跳到位置 i i i 的方案数
     f [ i ] = ∑ j = 0 i − d f [ j ] f[i] = \sum_{j=0}^{i-d}{f[j]} f[i]=j=0idf[j]
    则可在 O ( L ) O(L) O(L) 的时间内求出 f [ i ] f[i] f[i]


    考虑一次攻击 ( t , p ) (t,p) (t,p)
    从 0 0 0 t t t 次到位置 p p p 的方案数,等同于放球问题
    相当于 p p p 个球放入 t t t 个盒子里,盒子互不相同,球相同
    根据隔板法,方案数为 C p + t − 1 p C_{p+t-1}^{p} Cp+t1p
    加入每次至少跳 d d d 步的限制,等同于少了 d t dt dt 个球
    方案数为 C p − d t + t − 1 p − d t = C p − d t + t − 1 t − 1 C_{p-dt+t-1}^{p-dt} = C_{p-dt+t-1}^{t-1} Cpdt+t1pdt=Cpdt+t1t1
    从 p p p 跳到 L L L 的方案数,等于从 0 0 0 跳到 L − p L-p Lp 的方案数 f [ L − p ] f[L-p] f[Lp]
    则一次攻击的贡献为 C p − d t + t − 1 t − 1 × f [ L − p ] C_{p-dt+t-1}^{t-1} \times f[L-p] Cpdt+t1t1×f[Lp]


    考虑多次攻击
    因为每次攻击的贡献只能是它作为第一次攻击时的方案数
    所以计算第 k k k 次攻击时,要减去前 k − 1 k-1 k1 次攻击作为第一次攻击、第 k k k 次攻击作为第二次攻击的方案数
    设 d p [ i ] dp[i] dp[i] 表示第 i i i 次攻击,从 0 0 0 t i t_i ti 次到 p i p_i pi 的方案数
     d p [ i ] = C p i − d t i + t i − 1 t i − 1 − ∑ j = 1 i − 1 C p i − p j − d ( t i − t j ) + t i − t j − 1 t i − t j − 1 × d p [ j ] dp[i] = C_{p_i-dt_i+t_i-1}^{t_i-1} - \sum_{j=1}^{i-1} {C_{p_i-p_j-d(t_i-t_j)+t_i-t_j-1}^{t_i-t_j-1}} \times dp[j] dp[i]=Cpidti+ti1ti1j=1i1Cpipjd(titj)+titj1titj1×dp[j]
    也就是减去从 0 0 0 t i − t j t_i-t_j titj 次到位置 p i − p j p_i-p_j pipj 的方案数
    也就得出了第 i i i 次攻击作为第一次攻击的前半部分贡献了


    最后答案就是:
     a n s = f [ L ] − ∑ i = 1 m d p [ i ] × f [ l − p i ] ans = f[L] - \sum_{i=1}^{m} {dp[i] \times f[l-p_i]} ans=f[L]i=1mdp[i]×f[lpi]

    时间复杂度(预处理逆元): O ( L + m 2 ) O(L+m^2) O(L+m2)

核心:思维dp与组合数的运用

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const ll mod = 998244353;
const int maxn = 1e7 + 5;
int m;
ll l, d;
ll dp[3005], f[maxn], sum[maxn];

struct node{
	ll t, p;
	bool operator < (const node &A){
		return t < A.t; 
	}
} a[3005];

const int maxf = 1e7 + 5;
ll fac[maxf], inv[maxf], invf[maxf];
void init(){
	fac[0] = inv[0] = invf[0] = 1;
	fac[1] = inv[1] = invf[1] = 1;
	for(rint i=2; i<maxf; i++){
		fac[i] = fac[i-1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
		invf[i] = inv[i] * invf[i-1] % mod;
	}
}

ll C(ll n, ll m){
	if(n<0 || m<0 || n<m) return 0;
	return fac[n] * invf[m] % mod * invf[n-m] % mod;
}

int main() {
	init();
	scanf("%lld%lld%d", &l, &d, &m);
	for(int i=1; i<=m; i++) scanf("%lld%lld", &a[i].t, &a[i].p);
	sum[0] = 1;
	for(int i=1; i<=l; i++){
		if(i < d) f[i] = 0;
		else f[i] = sum[i-d] % mod;
		sum[i] = (sum[i-1] + f[i]) % mod;
	}
	sort(a+1, a+1+m);
	ll ans = f[l];
	for(int i=1; i<=m; i++){
		ll pi = a[i].p, ti = a[i].t;
		dp[i] = C(pi-1ll*d*ti+ti-1, ti-1);
		for(int j=1; j<i; j++){
			ll pj = a[j].p, tj = a[j].t;
			dp[i] = dp[i] - dp[j] * C(pi-pj-1ll*d*(ti-tj)+ti-tj-1, ti-tj-1) % mod;
			dp[i] = (dp[i] + mod) % mod;
		}
		ans = (ans - dp[i] * f[l - pi] % mod + mod) % mod;
	}
	printf("%lld\n", ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值