Loading

CF809D.Hitchhiking in the Baltic States

题意

给出 \(n\) 个区间 \([l_i,r_i]\)\(n\) 个未知数 \(a_1,a_2,\dots,a_n\),现在你要确定这 \(n\) 个数,使得 \(a_i\in[l_i,r_i]\),并且这个序列的最长严格上升子序列尽可能大,求这个最大值。

\(1 \leq n\leq 3\times 10^5\)\(1 \leq l_i,r_i\leq 10^9\)

题解

首先考虑 \(n^2\) dp, 设 \(dp[i]\) 表示长度为\(i\)的最长上升子序列的末尾最少是多少。

那么转移方程如下。

  • \(dp[i - 1] < l, dp[i] = \min(dp[i], l)\)
  • $l <= dp[i - 1]< r, dp[i] = dp[i - 1] + 1) $
  • \(dp[i - 1] >= r, dp[i] = dp[i]\)

观察知 \(dp_i\) 具有单调性。

对于第三部分我们显然可以不管它。

对于第二部分, 相当于区间平移, 并且区间加 \(+1\).

对于第一部分,就是把第一个大于等于 \(l\) 的值变为 \(l\)

对于区间平移, 相当于在区间前加入一个元素, 并删除区间后的第一个元素。

综上, \(dp\) 转移相当于删除第一个大于等于 \(r\) 的元素,把 \(l \leq dp[i] < r\)\(dp[i]\)\(1\) , 然后在第一个大于等于 \(l\) 的位置前插入一个 \(l\)

因为涉及区间操作和插入删除, 考虑 \(splay\) 维护。

时间复杂度: \(\Theta(nlogn)\)

  1. 要注意哨兵节点大小, 不能就设1e9, 我调了几个小时。

代码

点击查看代码
#include <stdio.h>
#include <string.h>
#include <iostream>
const int N = 6e5, INF = 1e9;
using namespace std;

int n, dp[N + 5], l[N + 5], r[N + 5], Opt;
int cnt, ans;

void read(int &x) {
	x = 0; int w = 1; char c = getchar(); for(; c < '0' || c >  '9'; c = getchar()) if (c == '-') w =  -1;
	for(; c <= '9' && c >= '0'; c = getchar()) x = x * 10 + c - '0'; x *= w;
}

struct spla{
	int ch[N + 5][2], fa[N + 5], val[N + 5], tag[N + 5],rt, tot;
	void pushdown(int p) {
		if (tag[p]) {
			if (ch[p][0]) {val[ch[p][0]] += tag[p]; tag[ch[p][0]] += tag[p];}
			if (ch[p][1]) {val[ch[p][1]] += tag[p]; tag[ch[p][1]] += tag[p];}
			tag[p] = 0;
		}
	}
	int get(int x) {return x == ch[fa[x]][1];}
	void rotate(int x) {
		int y = fa[x], z = fa[y], k = get(x);
		ch[z][get(y)] = x; fa[x] = z;
		ch[y][k] = ch[x][k^1]; fa[ch[y][k]] = y;
		ch[x][k^1] = y; fa[y] = x;
	}
	void splay(int x, int k) {
		while(fa[x] != k) {
			int y = fa[x], z = fa[y];
			pushdown(z); pushdown(y); pushdown(x);
			if (z != k) {
				if (get(x) != get(y)) rotate(x);
				else rotate(y);
			}
			rotate(x);
		}
		if (!k) rt = x;
	}
	void insert(int &pos, int v, int f) {
		if (!pos) {
			pos = ++tot; fa[pos] = f; val[pos] = v;
			splay(pos, 0);
			return;
		}
		pushdown(pos);
		if (v < val[pos]) insert(ch[pos][0], v, pos);
		else insert(ch[pos][1], v, pos);
	}
	int find(int x) { //  找最后一个小于x
		int p = rt, ans = 0;
		while(p) {
			pushdown(p);
			if (val[p] < x) {
				ans = p; p = ch[p][1];
			}
			else p = ch[p][0];
		}
		return ans;
	}
	int Nxt(int x) {
		splay(x, 0); x = ch[x][1];
		while(ch[x][0]) x = ch[x][0];
		return x;
	}
	int Pre(int x) {
		splay(x, 0); x = ch[x][0];
		while(ch[x][1]) x = ch[x][1];
		return x;
	}
	void del(int x) {
		int u = Pre(x), v = Nxt(x);
		splay(u, 0); splay(v, u);
		fa[x] = 0;ch[v][0] = 0;
	}
}t;

int main() {
	read(n); 
	t.insert(t.rt, -INF * 2 - 10, 0); t.insert(t.rt, INF + INF, 0);
	int ans = n;
	for(int i = 1, l, r; i <= n; ++i) {
		scanf("%d%d", &l, &r);
		int u = t.find(l), v = t.find(r), nxt = t.Nxt(v);
		if (u ^ v) {
			t.splay(u, 0); t.splay(nxt, u);
			t.val[t.ch[nxt][0]]++; t.tag[t.ch[nxt][0]]++;
		}
		if (nxt != 2) {t.del(nxt); --ans;}
		t.insert(t.rt, l, 0);
	}
	printf("%d\n", ans);
	return 0;	
}
posted @ 2022-12-07 20:50  Absolutey  阅读(26)  评论(0)    收藏  举报