1. 程式人生 > >【CF809D】Hitchhiking in the Baltic States

【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[i][j]dp[i][j]表示考慮到第ii個位置 , 最後選擇的數是jj的最長上升子序列長度

然而這裡jj的範圍很大,顯然不可做

我們考慮換維

根據我們最長上身子序列的二分棧的做法 , 很顯然的結論是長度一樣的上升子序列 , 要最後一個數最小最好,這樣才更有可能變長

於是我們設dp[i][j]dp[i][j]表示考慮到第ii位 , 當前上升子序列的長度為 jj 的選擇的最後一個數的最小值是多少

轉移很簡單了(注意這裡dp實際上是一個字首max):
假設當前區間是 [l,r][l,r]
那麼我們就考慮上一位的情況:
首先對任意 jj , dp[i][j]=dp[i1][j]dp[i][j]=dp[i-1][j],這樣就不用考慮相同 jj 的轉移了, 因為其他的轉移和從j

1j-1轉移過來是等價的

  1. dp[i1][j1]&lt;ldp[i-1][j-1]&lt;l , dp[i][j]=min(dp[i][j],l)dp[i][j]=min(dp[i][j],l)
  2. dp[i1][j1]&gt;=l&ThickSpace;and&ThickSpace;dp[i1][j1]&lt;r,dp[i][j]=min(dp[i][j],dp[i1][j1]+1)dp[i-1][j-1]&gt;=l \;and\; dp[i-1][j-1]&lt;r,dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1)
  3. dp[i1][j1]&gt;=r,dp[i-1][j-1]&gt;=r , 無轉移

容易發現情況分為三段

並且我們容易發現一個結論 , 那就是對於一個 ii , 它的dp值隨jj的增長而嚴格單調遞增(因為序列嚴格上升)
顯然選的多最後的數就會越大

這樣的話dp的轉移就是對區間進行操作了,考慮用SplaySplay來維護

情況 1: 這個相當於找到一個小於ll的最後一個數的jj , 把j+1j+1賦值為 ll
情況2 : 這個相當與是找到dp值在 [l,r)[l,r) 區間內的一段jj,把他們的dp值增加1並且下標後移一位(注意可能影響到第三種情況,故要把大於等於rr的第一個jj給刪掉)

插入刪除都能做 , 下標後移是什麼呢? 其實並不用管 , 因為你會在前面插入一個數 , 下標自動就加1了 , 所以只用SplaySplay實現區間加法就行了

注意要在插入的時候判斷是否該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);
}