CS Academy Restricted Permutations

リンク : https://csacademy.com/contest/archive/task/restricted-permutations/

問題概要

Nが与えられ、N-1個の数字が与えられる。この数字はi番目の数字が1ならば、iより後ろにi+1が存在する、0ならばiより前にi+1が存在する。N-1個の数字が示す条件をすべて満たす長さNの順列の通り数を求めよ。

解説

例えば、(1,1,1,0)なら下の図のように考えられる。どこに4を入れるかのみを考えればよい。 f:id:akusyounin:20190216062311p:plain この図の通り考えると、通り数をも求めるだけなら、前から何番目に位置するかが重要であるように見える。そこで、dp[i][j]をiまでを使い、iまでの条件を満たし、iが前からj番目にある順列の通り数とすると、
i番目の条件が1の場合は
dp[i][j+1]=sum{k=0,1...j}{dp[i-1][k]}
i番目の条件が2の場合は
dp[i][j]=sum{k=j,j+1...i}{dp[i-1][k]}
となる。sumの中の部分は累積和をもっておくとO(1)で計算できるためO(n2)でこの問題が解ける。
余談だがO(n2 logn)を最初に提出し、TLEした...

LL n, ans,a[2100],dp[2100][2100],sum[2100];
int main() {
    cin >> n;
    rep(i, n - 1) {
        cin >> a[i];
    }
    dp[0][0] = 1;
    rep(i, n - 1) {
        rep(j, n) {
            sum[j + 1] = dp[i][j] + sum[j];
            sum[j + 1] %= MOD;
        }
        for (int j = 0; j <= i; j++) {
            if (a[i]) {
                dp[i + 1][j + 1] = sum[j + 1];
                dp[i + 1][j + 1] %= MOD;
            }
            else {
                dp[i + 1][j] = sum[n] - sum[j];
                dp[i + 1][j] = (dp[i + 1][j] + MOD) % MOD;
            }
        }
    }
    rep(i, n) {
        ans += dp[n-1][i];
        ans %= MOD;
    }
    cout << ans << endl;
    return 0;
}