CodeForces 809D Hitchhiking in the Baltic States(FHQ-Treap)
阿新 • • 發佈:2018-12-22
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; }