poj 3378 Crazy Thairs 动态规划

题目链接:http://poj.org/problem?id=3378

 

题目大意:

      找出原序列中满足:

1. 1 ≤ i < j < k < l < m ≤ N
2. Ai < Aj < Ak < Al < Am

      两个条件长度为5的子序列个数。

 

题目分析:

      长度为5,我们可以从长度为1开始推算,一直推到长度为5。

      首先,对于长度为1的,所有的数都满足条件。对于长度为2的,我们就可以这样算了:在ai出现之前,有多少个小于ai的数,就有多少以ai结束且长度为2的序列满足条件。同理,对于长度为3的,可以求出在ai之前满足(长度为2且ak<ai)条件的有多少,便可知以ai结尾的长度为3的有多少个;以此类推……

      在求ai之前小于ai的数时,可以用数状数组来优化。具体过程是:在求dp[i][j]时,dp[i][j] = sum(dp[i-1][k]); (1<=k<j && ak < ai),即在求和时用树状数组,在求完dp[i][j]后更新树状数组:update(aj, dp[i-1][j]),因为在dp[i][j]里记录的就是长度为i以j结束的序列有多少个。

      最后需要注意就是要用高精度了。

 

代码实现:


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define maxn 50005
#define M 100000000

typedef struct{
	int a, b, c;
}bint;

int a[maxn], h[maxn], n, len;
bint dp[6][maxn], c[maxn];

int cmp(const void* x, const void* y) {
	return *(int*)x - *(int*)y;
}

inline int bs(int v) {
	int* p = (int*)bsearch(&v, h + 1, len + 1, sizeof(int), cmp);
	return p - h;
}

inline bint add(bint a, bint b) {
	a.a += b.a; a.b += b.b; a.c += b.c;
	a.b += a.c / M; a.a += a.b / M;
	a.b %= M; a.c %= M;
	return a;
}

inline void print(bint a) {
	if (a.a) {
		printf("%d%08d%08d\n", a.a, a.b, a.c);
	} else if (a.b) {
		printf("%d%08d\n", a.b, a.c);
	} else {
		printf("%d\n", a.c);
	}
}
// 更新树状数组
inline void update(int t, bint v) {
	while (t <= n) {
		c[t] = add(c[t], v);
		t += t & -t;
	}
}
// 树状数组求和
inline bint sum(int t) {
	bint s = {0, 0, 0};
	while (t) {
		s = add(s, c[t]);
		t -= t & -t;
	}
	return s;
}

int main() {
	while (scanf("%d", &n) != EOF) {
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			h[i] = a[i];
		}
		// 离散化
		qsort(h + 1, n, sizeof(int), cmp);
		len = 1;
		for (int i = 2; i <= n; i++)
			if (h[i] != h[len])
				h[++len] = h[i];
		for (int i = 1; i <= n; i++)
			a[i] = bs(a[i]);

		// 对dp[1][i]进行初始化
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= n; i++)
			dp[1][i].c = 1;

		for (int i = 2; i <= 5; i++) {
			// 对树状数组c复用,每次归0
			memset(c, 0, sizeof(c));
			for (int j = i - 1; j <= n; j++) {
				dp[i][j] = sum(a[j] - 1); // 先求出dp[i][j]
				update(a[j], dp[i - 1][j]); // 再更新c
			}
		}
		// dp[5][]里的结果之和就是最后结果
		bint res = {0, 0, 0};
		for (int i = 5; i <= n; i++)
			res = add(res, dp[5][i]);
		print(res);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值