HDU 5324 Boring Class(分治+树状数组)



题意: 定两个长度为n的数组,L,R,要求一个子序列(可以不连续)使的L递减,R递增。

思路:这道题可以先把R数组离散化转化为二维坐标排序问题类似于HDU4742 

这道题的不同之处在于要求出最后的这个序列下标并且要求字典序最小,所以要改变一下DP的含义,原来的dp[i]表示的含义是以i为结尾的lis,现在改为以i开头的最长下降子序列。这样在分治的时候,只需要先处理区间[M+1, R]然后再用这一部分的dp值更新前一段的dp值即可,dp记录两个值,第一个值是当前递降序列的长度,第二个是当前位置开始的递降序列的下一个元素的下标,其余部分类似于hdu4742.

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#define mp(a,b) make_pair (a , b)  
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 50100;
const int INF = 0x3f3f3f3f;
int n, Hash[MAXN];
pii dp[MAXN];
pii C[MAXN];
struct Node {
	int y, z, id;
	bool operator < (const Node& A) const {
		if(y==A.y) return z < A.z;
		return y < A.y;
	}
} node[MAXN], tmp[MAXN];
int lowbit(int x) {
	return x&-x;
}
void update(int x, pii A) {
	while(x <= n) {
		if(A.first>C[x].first || A.first==C[x].first&&A.second<C[x].second)
			C[x] = A;
		x += lowbit(x);
	}
}
pii query(int x) {
	pii ans = mp(0, INF);
	while(x > 0) {
		if(ans.first<C[x].first || ans.first==C[x].first&&ans.second>C[x].second) 
			ans = C[x];
		x -= lowbit(x);
	}
	return ans;
}
void clr(int x) {
	while(x <= n) C[x] = mp(0, INF), x += lowbit(x);
}
void solve(int L, int R) {
	if(L == R) return;
	int M = (L+R) >> 1;
	solve(M+1, R);
	int cnt = 0;
	for(int i = L; i <= R; i++) tmp[++cnt] = node[i];
	sort(tmp+1, tmp+cnt+1);
	for(int i = 1; i <= cnt; i++) {
		if(tmp[i].id > M) {
			pii shit = dp[tmp[i].id];
			shit.second = tmp[i].id;
			update(tmp[i].z, shit);
		}
		else {
			pii t = query(tmp[i].z);
			t.first++;
			if(t.first>dp[tmp[i].id].first || t.first==dp[tmp[i].id].first&&t.second<dp[tmp[i].id].second) 
				dp[tmp[i].id] = t;
		}
	}
	for(int i = 1; i <= cnt; i++) {
		if(tmp[i].id > M) clr(tmp[i].z);
	}
	solve(L, M);
}
void init() {
	for(int i = 1; i <= n; i++) {
		C[i] = mp(0, INF);
		dp[i] = mp(1, 0);
	}
}
int main() {
    //freopen("input.txt", "r", stdin);
	while(cin >> n) {
		init();
		for(int i = 1; i <= n; i++) scanf("%d", &node[i].y);
		for(int i = 1; i <= n; i++) scanf("%d", &node[i].z), Hash[i] = node[i].z;
		sort(Hash+1, Hash+n+1);
		int hs = unique(Hash+1, Hash+n+1) - Hash - 1;
		for(int i = 1; i <= n; i++) node[i].z = hs + 1 - (lower_bound(Hash+1, Hash+hs+1, node[i].z) - Hash);
		for(int i = 1; i <= n; i++) node[i].id = i;
		solve(1, n);
		pii ans = mp(0, INF);
		int flag = 0;
		for(int i = 1; i <= n; i++) {
			if(ans.first < dp[i].first) ans = dp[i], flag = i;
		}
		cout << ans.first << endl;
		printf("%d", flag);
		while(ans.second != 0) {
			printf(" %d", ans.second);
			ans = dp[ans.second];
		}
		puts("");
	}
    return 0;
}

















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值