luoguP4093、BZOJ4553[HEOI2016&TJOI2016]序列

题目链接

洛谷

BZOJ

建议交BZOJ,洛谷数据比较弱

解析

一看到这种长得跟最长上升子序列很像的东西就想到dp

不难写出dp方程:

\[dp[i] = max_{j < i, val[j] \le min[i],max[j] \le val[i]} \{dp[j] + 1\} \]

其中\(max[i]\),\(min[i]\),\(val[i]\)分别表示序列第\(i\)位的最大取值、最小取值和初始值

然后观察限制条件,发现是个三位偏序问题,就可以套CDQ了。。。。

代码(含注释)

PS.算是第一道自己想出来的CDQ吧,我好菜啊QAQ

再PS.CDQ都不会写了,最开始树状数组清零直接memset,结果T飞QAQ

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 100005

typedef long long LL;
struct Node {
	int val, mx, mn, id;
	bool operator <(const Node &t) const { return mn < t.mn; }
} a[MAXN], b[MAXN];
int dp[MAXN], tree[MAXN];
int N, M, ans;

void solve(int, int);
void update(int, int);
int query(int);
int main() {
	std::ios::sync_with_stdio(false);
	std::cin >> N >> M;
	for (int i = 1; i <= N; ++i) {
		std::cin >> a[i].val;
		a[i].mx = a[i].mn = a[i].val;
		a[i].id = i;
	}
	while (M--) {
		int x, y;
		std::cin >> x >> y;
		a[x].mx = std::max(a[x].mx, y);
		a[x].mn = std::min(a[x].mn, y);
	}
	//保证分治的时候右半区间mn有序 
	std::sort(a + 1, a + 1 + N);
	solve(1, N);
	for (int i = 1; i <= N; ++i)
		ans = std::max(ans, dp[i]);
	std::cout << ans << std::endl;
	return 0;
}
void solve(int l, int r) {
	if (l == r) {
		dp[a[l].id] = std::max(dp[a[l].id], 1);
		return;
	}
	int mid = (l + r) >> 1, p1, p2;
	
	//保证左半区间位置在右半区间之前,对应条件一 
	p1 = l, p2 = mid + 1;
	for (int i = l; i <= r; ++i)
		if (a[i].id <= mid) b[p1++] = a[i];
		else b[p2++] = a[i];
	for (int i = l; i <= r; ++i)
		a[i] = b[i];
	
	//先算出左半区间的dp值,并按val排序 
	solve(l, mid);
	
	//更新右半区间的dp值 
	p1 = l, p2 = mid + 1;
	while (p2 <= r) {
		while (p1 <= mid && a[p1].val <= a[p2].mn)//保证条件二成立 
			update(a[p1].mx, dp[a[p1].id]), ++p1;
		dp[a[p2].id] = std::max(dp[a[p2].id], query(a[p2].val) + 1);//这一行和上面一行保证条件三成立 
		++p2;
	}
	for (int i = l; i <= mid; ++i)
		update(a[i].mx, 0);//还原树状数组,直接memset会T飞。。。 
	
	//继续计算右半区间的dp值,并按val排序 
	solve(mid + 1, r);
	
	//将[l,r]按val排序,过程类似归并排序 
	p1 = l, p2 = mid + 1;
	for (int i = l; i <= r; ++i)
		if (p1 > mid) b[i] = a[p2++];
		else if (p2 > r) b[i] = a[p1++];
		else b[i] = (a[p1].val < a[p2].val ? a[p1++] : a[p2++]);
	for (int i = l; i <= r; ++i)
		a[i] = b[i];
}
inline void update(int pos, int v) {
	for (int i = pos; i < MAXN; i += (i & -i))
		if (v) tree[i] = std::max(tree[i], v);
		else tree[i] = 0;
}
inline int query(int pos) {
	int res = 0;
	for (int i = pos; i; i -= (i & -i))
		res = std::max(tree[i], res);
	return res;
}
posted @ 2019-02-22 17:02  Rhein_E  阅读(121)  评论(0编辑  收藏  举报