考え方
- 「
dp[i][j]
= $i$番目まで選んだときに和が$j$となる場合の数」としたくなる - 不等式の両辺が動くと困るので,片方を固定したい.和については↑のようにdpで考えたいので,maxを固定したい.
$\{(A_{i}, B_{i})\}_{i}$を$A_{i}$についてソートして昇順に考えれば,「$i$番目まで選ぶ & $i$番目を必ず選ぶ」とき「$\max = A_{i}$」となる.dpを計算する各ステップで$\max$が固定されていれば,各ステップで適切な$j$の範囲のdpを答えに加算していけばよさそう.
$i$まで計算できているとき,$i+1$は
\begin{aligned}
\mathrm{dp}[i+1][j+b]
&= \underbrace{\mathrm{dp}[i][j]}_{B_{i}\text{を選ぶ場合}}
+ \underbrace{\mathrm{dp}[i][j+b]}_{B_{i}\text{を選ばない場合}}
\end{aligned}
で求められる(dp[i]は$i$を選んでも選ばなくてもよい).\mathrm{dp}[i+1][j+b]
&= \underbrace{\mathrm{dp}[i][j]}_{B_{i}\text{を選ぶ場合}}
+ \underbrace{\mathrm{dp}[i][j+b]}_{B_{i}\text{を選ばない場合}}
\end{aligned}
dp[i]がわかれば,「$i$番目まで選ぶ & $i$番目を必ず選ぶ」ときの答えは
\begin{aligned}
\sum_{j=0}^{A_{i} - B_{i}} \mathrm{dp}[i - 1][j]
\end{aligned}
で計算できる($i$番目を必ず選ぶことは,$B_{i}$を予め引くことで表現できる).\sum_{j=0}^{A_{i} - B_{i}} \mathrm{dp}[i - 1][j]
\end{aligned}
よって,最終的に求めたいのは,すべての$i$の寄与を足し合わせた
\begin{aligned}
\sum_{\substack{i \\ (A_{i} \geq B_{i}) }}
\sum_{j=0}^{A_{i} - B_{i}} \mathrm{dp}[i - 1][j]
\pmod M
\end{aligned}
となる.\sum_{\substack{i \\ (A_{i} \geq B_{i}) }}
\sum_{j=0}^{A_{i} - B_{i}} \mathrm{dp}[i - 1][j]
\pmod M
\end{aligned}
回答例
dp[i + 1] = dp[i]
とすると,同じリストを参照することになりバグる.$j$の範囲は$\max A_{i}$まであれば十分.
N = int(input()) A = list(map(int, input().split())) B = list(map(int, input().split())) AB = sorted([(a, b) for a, b in zip(A, B)]) mod = 998244353 dp = [[0] * 5001 for _ in range(N + 1)] dp[0][0] = 1 ans = 0 for i in range(N): a, b = AB[i] for j in range(5001): dp[i + 1][j] += dp[i][j] dp[i + 1][j] %= mod if j + b <= 5000: dp[i + 1][j + b] += dp[i][j] dp[i + 1][j + b] %= mod if j + b <= a: ans += dp[i][j] ans %= mod print(ans)
【参考】Submission #25432523 - AtCoder Beginner Contest 216
参考
アライグマ「Aに同じ値があるときは、この問題と同じで、勝手にちょっと増やしたと思って適当に順番を決めればいいのだ!」https://t.co/Sjw8jzNt7K
— 競技プログラミングをするフレンズ (@kyopro_friends) August 29, 2021