【HEOI2016】序列

题面

题解

很像最长不下降子序列对吧(废话)

设$up[i]$和$down[i]$分别表示$i$最大最小能取多少

注意到:

$$ f[i] = max_j\left\{f[j]\right\} + 1 \\ a[j] \leq down[i],\; up[j] \leq a[i],\; j \leq i $$

三位偏序!!!

$CDQ$分治走起

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

using std::max;
const int maxn(100010);
int n, m, a[maxn], up[maxn], down[maxn], c[maxn], f[maxn], Max;
inline bool cmp_1(int x, int y) { return a[x] < a[y]; }
inline bool cmp_2(int x, int y) { return down[x] < down[y]; }
void clean(int x) { while(x <= Max) c[x] = 0, x += x & -x; }
void add(int x, int v) { while(x <= Max) c[x] = max(c[x], v), x += x & -x; }
int query(int x)
{
	int ans = 0;
	while(x) ans = max(ans, c[x]), x -= x & -x;
	return ans;
}

void Div(int l, int r)
{
	static int id[maxn];
	if(l == r) return (void)(f[l] = max(f[l], 1));
	int mid = (l + r) >> 1; Div(l, mid);
	for(RG int i = l; i <= r; i++) id[i] = i;
	std::sort(id + l, id + mid + 1, cmp_1);
	std::sort(id + mid + 1, id + r + 1, cmp_2);
	int j = l;
	for(RG int i = mid + 1; i <= r; i++)
	{
		while(j <= mid && a[id[j]] <= down[id[i]]) add(up[id[j]], f[id[j]]), j++;
		f[id[i]] = max(f[id[i]], query(a[id[i]]) + 1);
	}
	for(RG int i = l; i < j; i++) clean(up[id[i]]);
	Div(mid + 1, r);
}

int main()
{
	n = read(), m = read();
	for(RG int i = 1; i <= n; i++) up[i] = down[i] = a[i] = read();
	for(RG int i = 1, x, y; i <= m; i++)
		x = read(), y = read(),
		  up[x] = max(up[x], y),
		  down[x] = std::min(down[x], y);
	Max = *std::max_element(up + 1, up + n + 1);
	Div(1, n); printf("%d\n", *std::max_element(f + 1, f + n + 1));
	return 0;
}
posted @ 2019-01-09 21:54  xgzc  阅读(193)  评论(0编辑  收藏  举报