1. 程式人生 > >CodeForces 809D Hitchhiking in the Baltic States(FHQ-Treap)

CodeForces 809D Hitchhiking in the Baltic States(FHQ-Treap)

https://blog.csdn.net/wzf_2000/article/details/78343341?utm_source=blogxgwz0

題意

給你長度為$n$的序列,序列中的每個元素$i$有一個區間限制$[l_i,r_i]$,你從中選出一個子序列,並給它們標號$x_i$,要求滿足 $,∀i<j,x_i<x_j$,且$, ∀i,x_i∈[l_i,r_i]$。 問滿足條件子序列的長度最長為多少?

其中$1\leq n\leq3\times 10^5\ 1\leq l_i\leq r_i\leq 10^9$

題解

不妨設$f[i][j]$表示已經選到第$i$個,其中最大值為$j$最多能選幾個。

顯然是開不下的...但是還記得$O(nlogn)$的$LIS$嗎?它利用了二分棧!

所以我們不妨考慮設$f[i][j]$表示當前選到第$i$個,已經選了$j$個的末尾最小元素。

如果不選,顯然$f[i+1][j]=f[i][j]$

如果$r[i+1]>f[i][j]$,那麼,則有$f[i+1][j+1]=max(f[i][j]+1,l[i+1])$

不難發現可以用滾動陣列滾掉第一維,所以第一個方程直接作廢:

$ f[j+1]=max(f[j]+1,l[i+1]) $

但是時間還是$O(n^2)$的啊,列舉一個$i$,列舉一個$j$。

不急,咱們分開考慮,對於一個$l[i]<f<r[i]$

它的當前$dp$值可以直接$+1$,那麼位置也將$+1$因為多選了一個嘛。

剩下的話,就是$f<l[i]$的情況,直接就等於$l[i]$了。

用一顆$FHQ-Treap$維護一下就行了。

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

template<typename T>
void read(T &x) {
    int flag = 1; x = 0; char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') flag = -flag; ch = getchar(); }
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); x *= flag;
}

const int N = 3e5 + 10, Inf = 1e9 + 7;
int n, l, r;
int rt, tot, val[N], add[N], lc[N], rc[N], siz[N], pri[N];

inline int node(int x) { val[++tot] = x, siz[tot] = 1, pri[tot] = rand(); return tot; }
inline void pushdown(int o) {
	if(add[o]) val[o] += add[o], add[lc[o]] += add[o], add[rc[o]] += add[o], add[o] = 0;
}
inline void upt(int o) { siz[o] = siz[lc[o]] + siz[rc[o]] + 1; }
int merge(int l, int r) {
	if(!l || !r) return l + r;
	pushdown(l), pushdown(r);
	if(pri[l] < pri[r]) { rc[l] = merge(rc[l], r), upt(l); return l; }
	else { lc[r] = merge(l, rc[l]), upt(r); return r; }
}
void split(int o, int k, int &l, int &r) {
	if(!o) { l = r = 0; return ; } pushdown(o);
	if(val[o] <= k) l = o, split(rc[o], k, rc[o], r);
	else r = o, split(lc[o], k, l, lc[o]);
	upt(o);
}
int min(int o) { pushdown(o); return lc[o] ? min(lc[o]) : val[o]; }
int calc(int o) {
	if(!o) return 0; pushdown(o);
	return calc(lc[o]) + calc(rc[o]) + (val[o] < Inf);
}

int main () {
	read(n), srand(19260817); int x, y, k, p;
	for(int i = 1; i <= n; ++i) rt = merge(rt, node(Inf));
	for(int i = 1; i <= n; ++i) {
		read(l), read(r);
		split(rt, l, x, k), split(k, r, k, y);
		if(k) add[k] += 1;
		split(y, min(y) + 1, p, y);
		rt = merge(merge(x, node(l)), merge(k, y));
	} printf("%d\n", calc(rt));
    return 0;
}