【ybt高效进阶4-2-3】【luogu UVA12983】严格上升子序列数 / The Battle of Chibi

严格上升子序列数 / 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,j1)
那我们就枚举 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/114772037