【CF809D】Hitchhiking in the Baltic States
題目描述
給你長度為 n 的序列,序列中的每個元素 i 有一個區間限制 [li,ri],你從中選出一個子序列,並給它們標號 xi,要求滿足 ∀i<j,xi<xj,且 ∀i,xi∈[li,ri]。 問滿足條件子序列的長度最長為多少? 1≤n≤3×10^5 , 1≤li≤ri≤10 ^9。
Sol
這是一道毒瘤題(調好久QAQ)
就是讓你求一個最長嚴格上升子序列 , 只不過是每個數有一個取值範圍
先想到一個和原來方法類似的dp
設表示考慮到第個位置 , 最後選擇的數是的最長上升子序列長度
然而這裡的範圍很大,顯然不可做
我們考慮換維
根據我們最長上身子序列的二分棧的做法 , 很顯然的結論是長度一樣的上升子序列 , 要最後一個數最小最好,這樣才更有可能變長
於是我們設表示考慮到第位 , 當前上升子序列的長度為 的選擇的最後一個數的最小值是多少
轉移很簡單了(注意這裡dp實際上是一個字首max):
假設當前區間是
那麼我們就考慮上一位的情況:
首先對任意 , ,這樣就不用考慮相同 的轉移了, 因為其他的轉移和從轉移過來是等價的
- 若 ,
- 若
- 若
容易發現情況分為三段
並且我們容易發現一個結論 , 那就是對於一個 , 它的dp值隨的增長而嚴格單調遞增(因為序列嚴格上升)
顯然選的多最後的數就會越大
這樣的話dp的轉移就是對區間進行操作了,考慮用來維護
情況 1: 這個相當於找到一個小於的最後一個數的 , 把賦值為
情況2 : 這個相當與是找到dp值在 區間內的一段,把他們的dp值增加1並且下標後移一位(注意可能影響到第三種情況,故要把大於等於的第一個給刪掉)
插入刪除都能做 , 下標後移是什麼呢? 其實並不用管 , 因為你會在前面插入一個數 , 下標自動就加1了 , 所以只用實現區間加法就行了
注意要在插入的時候判斷是否該dp值已經存在 , 已存在則不滿足dp單調的性質 , 要退出不插入
然後就可以愉快地敲起 Splay 了
注意不要被這種資料卡了 QAQ
2999999
1 1000000000
1 1000000000
....
所以每次操作完後隨機一個點Splay到根,就可以跑得飛快了…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<set>
#include<queue>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
const int N=3e5+10;
int n;int l[N],r[N];
#define __ NULL
#define ls son[0]
#define rs son[1]
#define get_size(a) (a? a->size:0)
#define get_son(a) (a->fa->rs==a)
#define get_dp(a) (a? a->dp:-1)
struct node{
node* son[2];node* fa;int tag;int size;int dp;
inline void clear(){ls=rs=fa=__;tag=size=dp=0;}
}pool[N];int cnt=0;
node* rt;
inline void update(node* p){p->size=get_size(p->ls)+get_size(p->rs)+1;}
inline void down(node* p)
{
if(!p) return ;if(!p->tag) return;
const int x=p->tag;
if(p->ls) p->ls->tag+=x,p->ls->dp+=x;
if(p->rs) p->rs->tag+=x,p->rs->dp+=x;
p->tag=0;return;
}
inline void rotate(node* p){if(!p) return;
register int k=get_son(p);register node* q=p->fa;
down(q);down(p);q->son[k]=p->son[k^1];
if(p->son[k^1]) p->son[k^1]->fa=q;
if(q->fa) q->fa->son[get_son(q)]=p;
p->fa=q->fa;q->fa=p;p->son[k^1]=q;
return update(q);
}
inline void Splay(node* p,node* goal)
{
if(!p) return;down(p);if(goal==__) rt=p;
for(;p->fa!=goal;rotate(p)) if(p->fa->fa==goal) continue;else get_son(p)==get_son(p->fa)? rotate(p->fa):rotate(p);
return update(p);
}
inline node* Find_pre(int X){node* p=rt,*pre=__;while(p) {down(p);if(get_dp(p)>=X) p=p->ls;else pre=p,p=p->rs;} return pre;}
inline node* Find_nxt(int X){node* p=rt,*pre=__;while(p) {down(p);if(get_dp(p)< X) p=p->rs;else pre=p,p=p->ls;} return pre;}
inline void insert(int x)
{
if(!rt){rt=&pool[cnt++];rt->clear();rt->size=1;rt->dp=x;return;}
register node* p=rt,*pre=__;
while(p){
pre=p;
if(p->dp==x) return;down(p);
if(p->dp>x) p=p->ls;else p=p->rs;
}
p=&pool[++cnt];p->clear();p->size=1;p->dp=x;p->fa=pre;
if(pre->dp>x) pre->ls=p; else pre->rs=p;
return Splay(p,__);
}
inline void Del(node* p)
{
if(!p) return;Splay(p,__);node* q=p->ls;
while(q&&q->rs) q=q->rs;
if(!q) {rt=p->rs;if(p->rs) p->rs->fa=__;p->clear();}
else {
Splay(q,__);
if(p->rs) p->rs->fa=q;if(q) q->rs=p->rs;
update(q);p->clear();
}
return;
}
int main()
{
srand(19260817);
n=read();rt=__;
for(int i=1;i<=n;++i) l[i]=read(),r[i]=read();
if(!n) return puts("0"),0;
insert(l[1]);
for(register int i=2;i<=n;++i) {
register int L=l[i],R=r[i];
register node* p=Find_pre(L);
register node* q=Find_nxt(R);
Splay(p,__);Splay(q,p);
if(!p&&!q) {rt->tag+=1;rt->dp+=1;}
else {
if(!q) {if(p->rs) p->rs->dp+=1,p->rs->tag+=1;}
else if(q->ls) q->ls->dp+=1,q->ls->tag+=1;Del(q);
}
insert(L);
p=&pool[rand()%i+1];
if(p->size) Splay(p,__);
}
register node* p=rt;if(!p) return puts("0"),0;
while(p->rs) p=p->rs;Splay(p,__);
printf("%d\n",get_size(p->ls)+1);
}