题意:给你一个n个数的序列,要求从中找出含m个数的严格递增子序列,求能找出多少种不同的方案
dp[i][j]表示以第i个数结尾,形成的严格递增子序列长度为j的方案数
那么最终的答案应该就是sigma(dp[i][m]);
显然有:dp[i][j] = sigma(dp[k][j - 1]); 其中 1 <= k < i 且 a[k] < a[i];
题目要求严格递增,这个限制怎么解决?
hdu4719这道题同样是这样处理,即我们只需要从小到大dp就行了。
但是复杂度是O(n^3)的,显然需要优化,注意到应该从小到大dp之后,我们要做的就是快速求出sigma(dp[k][j-1]) (还未计算到的dp值为0) (注意不能直接用数组维护前缀和)
可以用树状数组得到优化,最终复杂度是O(n^2logn)
#include <bits/stdc++.h> #define lowbit(x) (x) & (-x) using namespace std; const int N = 1005; const int M = 1e9 + 7; int dp[N][N], c[N][N]; int a[N], r[N]; bool cmp(int b, int c) { return a[b] < a[c]; } void update(int i, int j, int value) { while(i < N) { c[i][j] = c[i][j] + value % M; i += lowbit(i); } } int sum(int i, int j) { int s = 0; while(i > 0) { s = s + c[i][j] % M; i -= lowbit(i); } return s % M; } int main() { int n, m; int _, cas = 1; scanf("%d", &_); while(_ --) { scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); memset(c, 0, sizeof c); memset(dp, 0, sizeof dp); for(int i = 0; i <= n; ++i) r[i] = i; sort(r + 1, r + n + 1, cmp); for(int i = 1; i <= n; ++i) { int id = r[i]; dp[id][1] = 1; update(id, 1, 1); for(int j = 2; j <= m; ++j) { dp[id][j] = sum(id - 1, j - 1); update(id, j, dp[id][j]); } } int ans = 0; for(int i = 1; i <= n; ++i) ans = ans + dp[i][m] % M; printf("Case #%d: %d ", cas++, ans % M); } return 0; }