HDU6880 Permutation Counting 【思维+dp】

题目链接
题目描述
存在长度为n的排列,给定一个长度(n - 1)的排列满足以下两种情况
1、(a_i>a_{i-1}, b[i] = 1)
2、(a_i < a_{i_1}, b[i] = 0)
现在给一个长度为(n-1)的b序列,问有多少种排列满足条件
思路
比赛时没有想到,赛后看了题解。
构造方法:初始状态下存在一个1,若b[i]==1,那么2就放在1的左边,否则2就放在1的右边,依次推类。
以样例1 0为例,先放一个1,b[1]=1,此时序列就变成了2 1, b[2]=0,那么3就放在2的右边,有以下两种情况, 2 3 1 / 2 1 3. 这样构成的序列叫一个p序列的话,那么就表示第(i)个数字应当放在第(p_i)位,所以排列种类数就等价于求构成这种p序列的方案数。
dp[i][j]表示数字(i)在第(j)位的所有方案数。
那么若b[i]=1,dp[i][j] = (sum_{k=j+1}^{k=i}dp[i-1][k])
若b[i]=0,dp[i][j] = (sum_{k=1}^{k=j-1}dp[i-1][k])
但是此时是(O(n^3))的复杂度,直接跑会TLE,可以通过一个sum的初始将复杂度降到(O(n^2))
代码

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 5010;
LL dp[N][N];
int b[N];

void solve() {
    int n; scanf("%d", &n);
    for(int i = 2; i <= n; i++) {
        scanf("%d", &b[i]);
    }
    memset(dp, 0, sizeof dp);
    dp[1][1] = 1;
    for(int i = 2; i <= n; i++) {
        LL sum = 0;
        if(b[i]) {
            for(int j = 1; j <= i; j++) {
                sum = (sum + dp[i - 1][j]) % MOD;
            }
            for(int j = 1; j <= i; j++) {
                dp[i][j] = (dp[i][j] + sum) % MOD;
                sum = (sum - dp[i - 1][j] + MOD) % MOD;
                // for(int k = j; k <= i; k++) {
                //     dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD;
                // }
            }
        } else {
            for(int j = 1; j <= i; j++) {
                dp[i][j] = (dp[i][j] + sum) % MOD;
                sum = (sum + dp[i - 1][j]) % MOD;
                // for(int k = 1; k <= j - 1; k++) {
                //     dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD;
                // }
            }
        }
    }
    LL res = 0;
    for(int i = 1; i <= n; i++) {
        res = (res + dp[n][i]) % MOD;
    }
    printf("%lld
", res);
}

int main() {
    // freopen("in.txt", "r", stdin);
    int t; cin >> t; while(t--)
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/ZX-GO/p/13559526.html