题目链接:http://acm.uestc.edu.cn/#/problem/show/1217
题目大意就是求一个序列里面长度为m的递增子序列的个数。
首先可以列出一个递推式p(len, i) = sum(p(len-1, j)) (a[j] < a[i])
p(len, i)表示以第i个结尾的长度为len的子序列的个数。
但是如果按照递增子序列的思想,然后直接弄的话,复杂度是n*m*n的。
如果需要优化的话,可以优化的地方就是那个求sum的过程。
把p数组映射到树状数组,那么求和的过程就能在logn时间完成。
这样写完是m*logn,一不小心可能写成nlogn的。。
不过这样竟然还是T(比赛的时候能过,不知道是评测机性能不一样,还是加数据了)。
需要加一个剪枝,就是从长度1到m枚举时,当某次查询的结果为0时,说明前面的子序列长度都达不到这个长度了,那么更长的显然也达不到了,于是此时直接break了OK了。
代码:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #include <set> #include <map> #include <queue> #include <string> #define LL long long #define MOD 1000000007 using namespace std; const int maxN = 1005; int n, m; struct node { int v; int id; }a[maxN]; bool cmp(node x, node y) { if (x.v != y.v) return x.v < y.v; else return x.id > y.id; } LL d[maxN][maxN]; int lowbit(int x) { return x&(-x); } void add(int len, int id, LL pls) { while(id <= maxN)//id最大是maxN { d[len][id] += pls; d[len][id] %= MOD; id += lowbit(id); } } LL sum(int len, int to) { LL s = 0; while(to > 0) { s = (s+d[len][to])%MOD; to -= lowbit(to); } return s; } void input() { scanf("%d%d", &n, &m); int k; for (int i = 0; i < n; ++i) { scanf("%d", &k); a[i].id = i+1; a[i].v = k; } sort(a, a+n, cmp); memset(d, 0, sizeof(d)); } void work() { LL k; for (int i = 0; i < n; ++i) { add(1, a[i].id, 1); for (int j = 2; j <= m; ++j) { k = sum(j-1, a[i].id-1); if (!k) break; add(j, a[i].id, k); } } printf("%lld ", sum(m, n)); } int main() { //freopen("test.in", "r", stdin); int T; scanf("%d", &T); for (int times = 1; times <= T; ++times) { printf("Case #%d: ", times); input(); work(); } return 0; }