mayoko’s diary

プロコンとかいろいろ。

Codeforces Round #225 (Div. 1) D. Antimatter

こんな簡単に解けると思ってなかったので目からウロコでした。

解法

dp[n][sum] = ([1, n], [2, n], ..., [n-1, n] という区間で, 和が sum になる場合の数) というのを考えます(antimatter の分はマイナスで数えるので, 要するに dp[n][0] の和が答えになります。ただ, コード上 dp[5][-4] とかは出来ないので下のコードでは MAX 分だけオフセットをとっていると)。

まず, 普通に dp[n-1][sum] を使って, dp[n][sum+a], dp[n][sum-a] に場合の数を伝搬させる普通の dp をやります。で, あとは n 番目からのスタートする区間のことを考えないといけないですが, これは dp[n][-a]++, dp[n][a]++ とやれば「n 番目から初めて合計 a になるのは 1 通り」というのを考慮しているのでこれで OK です。頭良いですね。

const ll MOD = 1e9+7;
const int MAX = 10000;
ll dp[2][2*MAX+1];

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n, cur = 0, next = 1;
    ll ans = 0;
    cin >> n;
    for (int i = 0; i < n; i++) {
        int a;
        cin >> a;
        memset(dp[next], 0, sizeof(dp[next]));
        for (int i = 0; i <= 2*MAX; i++) if (dp[cur][i]) {
            (dp[next][i+a] += dp[cur][i]) %= MOD;
            (dp[next][i-a] += dp[cur][i]) %= MOD;
        }
        ans += dp[next][MAX];
        dp[next][MAX+a]++;
        dp[next][MAX-a]++;
        swap(cur, next);
    }
    cout << ans%MOD << endl;
    return 0;
}