给定一个长度为N的序列A,求A有多少个长度为M的严格递增子序列。
输入格式
第一行包含整数T,表示共有T组测试数据。
每组数据,第一行包含两个整数N和M。
第二行包含N个整数,表示完整的序列A。
输出格式
每组数据输出一个结果,每个结果占一行。
输出格式为“Case #x: y”,x为数据组别序号,从1开始,y为结果。
由于数据可能很大,请你输入对10^9+7取模后的结果。
数据范围
1≤T≤100,
1≤M≤N≤1000,
∑Ti=1Ni×Mi≤10^7
序列中的整数的绝对值不超过10^9。
输入样例:
2
3 2
1 2 3
3 2
3 2 1
输出样例:
Case #1: 3
Case #2: 0
TLE代码,实际上只是优化了寻找边界,减少第三层循环的大小,状态转移还是朴素的O(n^3)。
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N = 1e3 + 10, M = 1e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int T, n, m, cnt;
int a[N], f[N][M];
multiset<pii > heap;
inline void init() {
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++)f[i][1] = 1;
heap.clear(), heap.insert({INF, 0});
}
int main() {
cin >> T;
while (T--) {
scanf("%d%d", &n, &m), init();
for (int i = n; i >= 1; i--)scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
auto t = heap.upper_bound({a[i], 0});
while ((*t).fi != INF) {
for (int j = 2; j <= m; j++)
f[i][j] = (f[i][j] + f[(*t).se][j - 1]) % MOD;
t++;
}
heap.insert({a[i], i});
}
int ans = 0;
for (int i = m; i <= n; i++)ans += f[i][m];
printf("Case #%d: %d\n", ++cnt, ans);
}
return 0;
}
利用树状数组可以将第三层循环降到O(logn),最终总的时间复杂度为O(NMlogN)
#include <bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 1e3 + 10, MOD = 1e9 + 7;
int T, n, m, cnt;
int a[N], b[N], c[N];
int f[N][N], tot;
unordered_map<int, int> num;
inline int ask(int x) {
int res = 0;
for (; x; x -= lowbit(x))res = (res + c[x]) % MOD;
return res;
}
inline void add(int x, int y) {
for (; x <= n; x += lowbit(x))c[x] = (c[x] + y) % MOD;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
memcpy(b, a, sizeof(a));
sort(b + 1, b + 1 + n);
tot = unique(b + 1, b + 1 + n) - (b + 1);
for (int i = 1; i <= tot; i++)num[b[i]] = i;
for (int i = 1; i <= n; i++)a[i] = num[a[i]];
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++)f[i][1] = 1;
for (int j = 2; j <= m; j++) {
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; i++) {
f[i][j] = (f[i][j] + ask(a[i] - 1)) % MOD;
add(a[i], f[i][j - 1]);
}
}
int ans = 0;
for (int i = m; i <= n; i++)ans = (ans + f[i][m]) % MOD;
printf("Case #%d: %d\n", ++cnt, ans);
}
return 0;
}