【树状数组】Hdu5542 The Battle of Chibi

该博客介绍了一个利用树状数组解决数论问题的案例,具体是针对一个数列求长度为M的严格递增子序列的数量。题目要求在不超过10^9的范围内,计算所有可能的递增子序列,结果需要对10^9+7取模。博主分析了暴力解法,并提出了通过树状数组优化的解决方案,以适应题目所给的数据范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值