题意:对于1 - n的一个排列a,计算上升序列长度为1,2,....,n的序列的种类
分析:
(1)上升子序列问题,注意到与一般最长上升子序列问题不同的是(1) 计算各个长度的序列种类 (2) 只是1-n的排列
(2)容易想到的是先计算长度为i的以a[j]结尾的种类,记为dp[i][j],则dp[i][j] = (dp[i-1][k] + dp[i][j]) (当a[j] > a[k]且 j > k),
但是复杂度有 O(n ^ 3)
(3)思考优化:a只是1-n的排列,需要利用好这一特殊性
先思考能否从O(n^3) ---> O(n^2),则需要去掉转移方程中的k,也就是能否直接得到长度为i-1,结尾a[k]< a[j]且k < j的
上升子串的种类,也就是位于a[j]左边的满足长度为i-1,a[k] < a[j]的上升子串种类。
"位于左边的"容易实现,因为从左往右遍历,一边遍历一边计算上一轮长度位i-1的结果,此时"长度为i-1"这一条件也
就满足了。不容易满足的是"a[k] < a[j]"这一条件,因为是1-n的排列,a[j]的范围有限,可以采用前缀和的思想,引入
sum[]数组,sum[j]表示"a[k] <= a[j]"且满足其他两个条件的上升子串种类,但是sum[j]需要是动态更新的,必须从左往
右一边更新一边计算,可以想到,利用树状数组可以解决这一问题
#include <iostream> #include <cstdio> #include <cstring> #define MOD 1000000007 using namespace std; long long dp[10050]; long long C[10050]; int a[10050]; long long ans[10050]; int n; int lowbit(int x) { return x & (-x); } long long sum(int x) { long long ret = 0; while (x > 0) { ret = (ret + C[x]) % MOD; x -= lowbit(x); } return ret; } void add(int x,long long d) { while (x <= n) { C[x] = (C[x] + d) % MOD; x += lowbit(x); } } int main() { int T; int N; int t = 1; scanf("%d",&T); while (t <= T) { scanf("%d",&N); n = N; for (int i = 1;i <= N;i++) { scanf("%d",&a[i]); } //初始化 memset(dp,0,sizeof(dp)); memset(C,0,sizeof(C)); memset(ans,0,sizeof(ans)); for (int i = 1;i <= N;i++) { dp[i] = 1; } for (int i = 2;i <= N;i++) { memset(C,0,sizeof(C)); for (int j = 1;j <= N;j++) { add(a[j],dp[j]); dp[j] = 0; dp[j] = (dp[j] + sum(a[j] - 1)) % MOD; ans[i] = (ans[i] + dp[j]) % MOD; } //若ans[i]为0,ans[i + 1]一定为 0, if (ans[i] == 0) { break; } } printf("Case #%d:",t++); ans[1] = N; for (int i = 1;i <= N;i++) { printf(" %d",ans[i]); } printf("\n"); } return 0; }