传送门 : CF 579c
题解
dp
状态ans[i][j] 表示前i个数,包含j个数的子序列个数有多少
f[i][j]表示前i个数,以a[i]结尾的,包含j个数的子序列有多少
dp[i][j] = dp[i-1][j] + f[i][j] 这个转移很容易想到
f[i][j] = sum f[k][j-1] 其中1<=k < i 且 a[k] < a[i]
这个转移也不难理解,但是转移的复杂度是O(n)的了,我们用树状数组来解决问题
由于1<=a[i] <= n,所以我们可以以a[i]作为下标建立k个树状数组,第j个树a[i]的位置存f[i][j],当我们更新f[i]时,此时我们只需查询1~a[i]的前缀和即可,最后再用f[i]更新树的a[i]节点即可本质是树状数组对dp的优化
AC code:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define lowbit(x) (x & (-x))
typedef long long LL;
const int maxn = 100000 + 5;
int a[maxn], n, k;
LL ans[maxn][15], c[maxn][15], f[maxn][15];
void add(int x, LL v, int j) {
while (x <= n) {
c[x][j] += v;
x += lowbit(x);
}
}
LL sum(int x, int j) {
LL res = 0;
while (x) {
res += c[x][j];
x -= lowbit(x);
}
return res;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
while (cin >> n >> k) {
++k;
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; ++i) {
cin >> a[i];
ans[i][1] = i;
}
for (int i = 1; i <= n; ++i) {
for (int j = min(i, k); j >= 2; j--) {
f[i][j] = sum(a[i], j - 1);
add(a[i], f[i][j], j);
//这里是for循环更新f[i][j]优化
}
add(a[i], 1, 1);//f[i][1]
}
for (int i = 2; i <= n; ++i) {
for (int j = 2; j <= min(i, k); ++j) {
ans[i][j] = ans[i - 1][j] + f[i][j];
}
}
cout << ans[n][k] << endl;
}
return 0;
}