题目链接
题目描述
存在长度为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;
}