Description
给定一个长度为N的数列A,求A有多少个长度为M的严格递增子序列。1≤M≤N≤1000,序列A中的数的绝对值不超过
〖10〗^9。因为答案可能很大,你只需要输出对 〖10〗^9+7 取模后的结果。
Input
The first line of the input gives the number of test cases, T(1≤100). T test cases follow.
Each test case begins with two numbers N(1≤N≤103) and M(1≤M≤N),
indicating the number of information and number of information Gai Huang will select.
Then N numbers in a line, the ith number ai(1≤ai≤10^9)
indicates the value in Cao Cao's opinion of the ith information in happening order.
Output
For each test case, output one line containing Case #x: y,
where x is the test case number (starting from 1) and y is the ways Gai Huang can select the information.
The result is too large, and you need to output the result mod by 1000000007(10^9+7).
Sample Input
2 3 2 1 2 3 3 2 3 2 1
Sample Output
Case #1: 3 Case #2: 0
分析
首先我们考虑暴力算法。
状态设计成两维,dp[ i ][ k ]表示在区间[1, i]中,长度为k的递增子序列数量
状态转移方程如下
dp[i][k] += dp[j][k - 1];
代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int n, m;
int a[1010];
int dp[1010][1010];
int ans, cnt;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
int main() {
int t = read();
while (t--) {
memset(dp, 0, sizeof dp);
ans = 0;
n = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read(), dp[i][1] = 1;
for (int k = 1; k <= m; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (a[i] > a[j]) {
(dp[i][k] += dp[j][k - 1]) %= mod;
}
}
}
}
for (int i = 1; i <= n; i++) (ans += dp[i][m]) %= mod;
printf("Case #%d: %d\n", ++cnt, ans);
}
}
观察数据范围,考虑优化。
通过观察发现,第三重j循环可使用树状数组优化。
每次统计前缀和和修改时多传入一个参数y,表示计算长度为y的递增子序列的数量
代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
struct node {
int a, id;
} e[1010];
int n, m;
int pos[1010];
int tree[1010];
int dp[1010][1010];
int ans, cnt;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
int ask(int x) {
int sum = 0;
for (; x; x -= (x & -x)) sum += tree[x];
return sum;
}
void add(int x, int y) {
for (; x <= 1000; x += (x & -x)) tree[x] += y;
}
bool cmp(node a, node b) {
return a.a < b.a;
}
int main() {
int t = read();
while (t--) {
memset(dp, 0, sizeof dp);
ans = 0;
n = read() + 1, m = read();
for (int i = 2; i <= n; i++) e[i].a = read(), e[i].id = i;
sort(e + 2, e + n + 1, cmp);
for (int i = 2; i <= n; i++) pos[e[i].id] = i;
pos[1] = 1;
add(1, 1);
for (int k = 1; k <= m; k++) {
for (int i = 2; i <= n; i++) {
(dp[i][k] += ask(pos[i])) %= mod;
add(pos[i], dp[i][k - 1]);
}
memset(tree, 0, sizeof tree);
}
for (int i = 1; i <= n; i++) (ans += dp[i][m]) %= mod;
printf("Case #%d: %d\n", ++cnt, ans);
}
}