D - 均等な買い物 / Equal Shopping Editorial
by
harurun4635
簡単には \(O(N^{P+Q})\) 通りの選び方がありますが、これをすべて試すことはできません。しかし、結局はほしいものは subset sum で、値の範囲が小さいですから dp が有効であると考えられます。
\(dp[i][j][C_i][C_j] =\) 高橋くんは \(i\) 個購入し \(C_i\) 円、青木くんは \(j\) 個購入し \(C_j\) 円できる通り数
とした next dp では、状態が \(O(N \times P\times Q \times PR \times QR)\) 遷移が \(O(R)\) で、これを \(M\) 回解きます。
間に合うかの評価は少し大変ですが、額面では \(1.296 \times 10^9\) 程度になり、十分に高速な言語なら(特別な工夫なしに)間に合うでしょう。
しかし、簡単な改善があります。今回は、最終的に \(C_i = C_j\) にしか興味がないため、 \(D = C_i - C_j\) として \(dp[i][j][D]\) のかたちで管理すればよいです。
こうすることで、状態が \(O(N \times \ P \times Q \times (P + Q) R)\) に抑えることができます。これは、額面では \(10\) 倍程度の改善で、実行時間に余裕を持って間に合うでしょう。
実装例
Python では、負の index をもちいることができることを利用しています。( dp[-x] = dp[len(dp)-x])言語によっては、適切にこの式で書き直すか、 すべてに offset を加えることで、常に添字を \(0\) 以上にするなどの工夫が必要です。
どちらの場合においても、配列外参照については、十分に注意しなければならないと思われます。
mod = 998244353
n, p, q, m = map(int, input().split())
lr = [list(map(int, input().split())) for _ in range(n)]
def solve():
dp = [[[0] * 130 for _ in range(q + 1)] for _ in range(p + 1)]
dp[0][0][0] = 1
for l, r in lr:
ndp = [[[0] * 130 for _ in range(q + 1)] for _ in range(p + 1)]
for i in range(p + 1):
for j in range(q + 1):
for k in range(-60, 61):
v = dp[i][j][k] % mod
if v == 0: continue
ndp[i][j][k] += v
if i < p:
for x in range(l, r + 1):
ndp[i+1][j][k+x] += v
if j < q:
for x in range(l, r + 1):
ndp[i][j+1][k-x] += v
dp = ndp
return dp[-1][-1][0] % mod
for _ in range(m):
x, a, b = map(int, input().split())
lr[x-1] = [a, b]
print(solve())
posted:
last update: