题目链接:点我啊╭(╯^╰)╮
题目大意:
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=0i−df[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+t−1p
加入每次至少跳
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}
Cp−dt+t−1p−dt=Cp−dt+t−1t−1
从
p
p
p 跳到
L
L
L 的方案数,等于从
0
0
0 跳到
L
−
p
L-p
L−p 的方案数
f
[
L
−
p
]
f[L-p]
f[L−p]
则一次攻击的贡献为
C
p
−
d
t
+
t
−
1
t
−
1
×
f
[
L
−
p
]
C_{p-dt+t-1}^{t-1} \times f[L-p]
Cp−dt+t−1t−1×f[L−p]
考虑多次攻击
因为每次攻击的贡献只能是它作为第一次攻击时的方案数
所以计算第
k
k
k 次攻击时,要减去前
k
−
1
k-1
k−1 次攻击作为第一次攻击、第
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]=Cpi−dti+ti−1ti−1−∑j=1i−1Cpi−pj−d(ti−tj)+ti−tj−1ti−tj−1×dp[j]
也就是减去从
0
0
0 跳
t
i
−
t
j
t_i-t_j
ti−tj 次到位置
p
i
−
p
j
p_i-p_j
pi−pj 的方案数
也就得出了第
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[l−pi]
时间复杂度(预处理逆元): 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);
}