Codeforces #225(383 Div1) D. Antimatter
問題
長さnの数列a[i]が与えられる。
aの任意の空でない区間[l, r]で、a[i]の符号を好きに反転してよい。
このようにして[l, r]内の総和が0になるようにする方法は何通りあるか。
制約条件
n≦1000
-1000≦a[i]≦1000
a[i]の総和≦10000
方針
Div1 D史上最も酷い問題?
dp[pos][sum] := [x, pos]の和をsumにする場合の数。xは1〜pos全部
とするだけ。
ソースコード
const int mod = inf + 7; int n, a, dp[2][20001]; int main(){ scanf("%d", &n); int cur = 0, next = 1; ll ans = 0; rep(i, n){ scanf("%d", &a); memset(dp[next], 0, sizeof(dp[next])); rep(j, 20001) if(dp[cur][j]){ (dp[next][j + a] += dp[cur][j]) %= mod; (dp[next][j - a] += dp[cur][j]) %= mod; } ans += dp[next][10000]; dp[next][10000 + a]++; dp[next][10000 - a]++; swap(cur, next); } cout << ans % mod << endl; return 0; }
余談
writerの想定解は分割統治だったらしく、
(l, r)の場合の数は、m = (l + r) / 2を区間がまたがない場合とまたぐ場合。
前者は(l, m), (m, r)を解けばよく、
後者は、(l, m), (m, r)でナップサックをして、それぞれで和がxになる場合の数をcnt[x] = Σdp[i][x]としてまとめ、cnt[x]同士をかける。
これでO(n m log n).