洛谷链接
题目描述
给你长度为 n 的序列,序列中的每个元素 i 有一个区间限制 [li,ri],你从中选出一个子序列,并给它们标号 xi,要求满足 ∀i
Sol
这是一道毒瘤题(调好久QAQ)
就是让你求一个最长严格上升子序列 , 只不过是每个数有一个取值范围
先想到一个和原来方法类似的dp
设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示考虑到第 i i i个位置 , 最后选择的数是 j j j的最长上升子序列长度
然而这里 j j j的范围很大,显然不可做
我们考虑换维
根据我们最长上身子序列的二分栈的做法 , 很显然的结论是长度一样的上升子序列 , 要最后一个数最小最好,这样才更有可能变长
于是我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示考虑到第 i i i位 , 当前上升子序列的长度为 j j j 的选择的最后一个数的最小值是多少
转移很简单了(注意这里dp实际上是一个前缀max):
假设当前区间是 [ l , r ] [l,r] [l,r]
那么我们就考虑上一位的情况:
首先对任意 j j j , d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i−1][j],这样就不用考虑相同 j j j 的转移了, 因为其他的转移和从 j − 1 j-1 j−1转移过来是等价的
- 若 d p [ i − 1 ] [ j − 1 ] < l dp[i-1][j-1]<l dp[i−1][j−1]<l , d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , l ) dp[i][j]=min(dp[i][j],l) dp[i][j]=min(dp[i][j],l)
- 若 d p [ i − 1 ] [ j − 1 ] > = l    a n d    d p [ i − 1 ] [ j − 1 ] < r , d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − 1 ] + 1 ) dp[i-1][j-1]>=l \;and\; dp[i-1][j-1]<r,dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1) dp[i−1][j−1]>=landdp[i−1][j−1]<r,dp[i][j]=min(dp[i][j],dp[i−1][j−1]+1)
- 若 d p [ i − 1 ] [ j − 1 ] > = r , 无 转 移 dp[i-1][j-1]>=r , 无转移 dp[i−1][j−1]>=r,无转移
容易发现情况分为三段
并且我们容易发现一个结论 , 那就是对于一个 i i i , 它的dp值随 j j j的增长而严格单调递增(因为序列严格上升)
显然选的多最后的数就会越大
这样的话dp的转移就是对区间进行操作了,考虑用 S p l a y Splay Splay来维护
情况 1: 这个相当于找到一个小于 l l l的最后一个数的 j j j , 把 j + 1 j+1 j+1赋值为 l l l
情况2 : 这个相当与是找到dp值在 [ l , r ) [l,r) [l,r) 区间内的一段 j j j,把他们的dp值增加1并且下标后移一位(注意可能影响到第三种情况,故要把大于等于 r r r的第一个 j j j给删掉)
插入删除都能做 , 下标后移是什么呢? 其实并不用管 , 因为你会在前面插入一个数 , 下标自动就加1了 , 所以只用 S p l a y Splay Splay实现区间加法就行了
注意要在插入的时候判断是否该dp值已经存在 , 已存在则不满足dp单调的性质 , 要退出不插入
然后就可以愉快地敲起 Splay 了
注意不要被这种数据卡了 QAQ
2999999
1 1000000000
1 1000000000
....
所以每次操作完后随机一个点Splay到根,就可以跑得飞快了…
#include
#include
#include
#include
#include
#include
#include
#include
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);
}