严格上升子序列数 / The Battle of Chibi
题目链接:ybt高效进阶4-2-3 / luogu UVA12983
题目大意
要你在一个序列中找长度为 m 的严格上升子序列的个数。
个数对 1e9+7 取模。
思路
首先,看到这种求上升子序列个数(也就是有关逆序对顺序对的),就会想到用树状数组。
那我们考虑再和 DP 结合在一起(因为它固定了长度)。
那我们设 f i , j f_{i,j} fi,j 为对于以第 i i i 个数结尾的严格上升子序列,它的长度是 j j j,这样的有多少个。
然后容易搞出转移:
f
i
,
j
=
∑
a
k
<
a
i
,
k
<
i
(
f
k
,
j
−
1
)
f_{i,j}=\sum\limits_{a_k<a_i,k<i}(f_{k,j-1})
fi,j=ak<ai,k<i∑(fk,j−1)
那我们就枚举
i
,
j
i,j
i,j,对于
k
k
k 我们可以用树状数组来弄。
因为要树状数组,所以要离散化。
时间复杂度是 O ( n 2 l o g n ) O(n^2logn) O(n2logn),因为比较紧,取模的时候判断一下再取。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define mo 1000000007
using namespace std;
struct node {
int x, num, lx;
}a[1001];
int T;
int n, m;
ll ans, tree[1001][1001], dp[1001][1001];
void csh() {
ans = 0;
memset(tree, 0, sizeof(tree));
}
bool cmp1(node x, node y) {
return x.x < y.x;
}
bool cmp2(node x, node y) {
return x.num < y.num;
}
void lsh() {
sort(a + 1, a + n + 1, cmp1);
int tmp = 0;
a[1].lx = ++tmp;
for (int i = 2; i <= n; i++)
if (a[i].x == a[i - 1].x) a[i].lx = tmp;
else a[i].lx = ++tmp;
sort(a + 1, a + n + 1, cmp2);
}
void add(int x, int y, int z) {//树状数组
for (; x <= n; x += x & (-x)) {
tree[x][y] += z;
if (tree[x][y] > mo) tree[x][y] -= mo;
}
}
ll ask(int x, int y) {
ll re = 0;
for (; x; x -= x & (-x)) {
re += tree[x][y];
if (re > mo) re -= mo;
}
return re;
}
int main() {
scanf("%d", &T);
for (int times = 1; times <= T; times++) {
csh();//初始化
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].x);
a[i].num = i;
}
lsh();//离散化
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j == 1) dp[i][j] = 1;//长度为1,直接方案数为1
else {
dp[i][j] = ask(a[i].lx - 1, j - 1);
if (dp[i][j] > mo) dp[i][j] -= mo;//不要直接取模
}
add(a[i].lx, j, dp[i][j]);
}
}
for (int i = m; i <= n; i++) {
ans += dp[i][m];//看这个子序列最后是哪个结尾的个数
if (ans > mo) ans -= mo;
}
printf("Case #%d: %lld\n", times, ans);
}
return 0;
}