CodeForces 809D - Hitchhiking in the Baltic States
洛谷题目页面传送门 & CodeForces题目页面传送门
题意见洛谷里的翻译。
一脸DP的样,于是考虑DP。
与普通的LIS类似,设\(dp_{i,j}\)表示考虑到第\(i\)个数,最后一位为\(a_i=j\)的LIS长度。显然,第二维是定义在值域\(\left[1,10^9\right]\)上的,无论如何优化它都会出现在时间/空间复杂度上,受不起。于是考虑一种惯用套路,将DP数组的定义域和值域调换,即设\(dp_{i,j}\)表示考虑到第\(i\)个数,长度为\(j\)的IS的最后一位最小为多少(如果无法实现则为\(+\infty\))。
边界为\(dp_{0,i}=\begin{cases}0&i=0\\+\infty&i>0\end{cases}\),目标为\(\max\limits_{i\in[1,n],dp_{n,i}<+\infty}\{i\}\),状态转移方程为\(dp_{i,j}=\min\begin{cases}dp_{i-1,j}\\\max(l_i,dp_{i-1,j-1}+1)&dp_{i-1,j-1}<r\end{cases}\)(显然,基于贪心,若选\(a_i\),那么在可能情况下\(a_i\)越小越好)。这样空间复杂度可以用滚动数组压到\(\mathrm O(n)\),但时间复杂度是\(\mathrm O\!\left(n^2\right)\)的。考虑优化。
注意到转移时的最优决策只可能为\(dp_{i-1,j},l_i,dp_{i-1,j-1}+1\)这\(3\)种,于是分情况讨论:
- 转移到\(l_i\)。当且仅当\(\begin{cases}l_i\geq dp_{i-1,j-1}+1\\l_i\leq dp_{i-1,j}\end{cases}\)。不难发现,\(\forall i\in[0,n]\),\(dp_i\)不为\(+\infty\)的那部分是严格单调上升的(易证)。于是我们设\(las\)为最大的\(j\)使得\(dp_{i-1,j}<l_i\)。那么\(\begin{cases}l_i\geq dp_{i-1,j-1}+1\\l_i\leq dp_{i-1,j}\end{cases}\Rightarrow\begin{cases}j-1\leq las\\j>las\end{cases}\Rightarrow j=las+1\);
- 转移到\(dp_{i-1,j-1}+1\)。当且仅当\(\begin{cases}dp_{i-1,j-1}+1>l_i\\dp_{i-1,j-1}+1\leq dp_{i-1,j}\\dp_{i-1,j-1}<r\end{cases}\)(\(dp_{i-1,j-1}+1=l_i\)归在情况\(1\)里)。根据单调性,\(dp_{i-1,j-1}+1\leq dp_{i-1,j}\)显然恒成立。设\(las'\)为最大的\(j\)使得\(dp_{i-1,j}<r_i\)。那么\(\begin{cases}dp_{i-1,j-1}+1>l_i\\dp_{i-1,j-1}<r\end{cases}\Rightarrow\begin{cases}j-1>las\\j-1\leq las'\end{cases}\Rightarrow j\in[las+2,las'+1]\);
- 转移到\(dp_{i-1,j}\)。不满足情况\(1,2\)的条件则为此情况。
我们可以维护\(dp_i\)这个数列,每此转移即令\(dp_i=dp_{i+1}\)。不难发现,这样想的话,情况\(3\)相当于什么也不做,正好我们也没分析情况\(3\)的充要条件,于是只需要考虑支持情况\(1,2\)对应的操作即可。不难想到可以用平衡树维护,这里使用fhq-Treap。情况\(1\)就是个单点修改;情况\(2\)可以转化为剖出区间\([las+1,las']\)后,删除第\(las'+1\)个元素并将第\(las+1\)个元素在原位拷贝一份,然后区间增加\(1\)。与情况\(2\)一综合,可以把“单点修改、将第\(las+1\)个元素在原位拷贝一份”简化成“在第\(las+1\)个元素的位置添加”(当然不简化也没关系)。这里面涉及到删除操作,由于本憨憨不想算总共会新建多少个节点,而且正好找个机会练习一下,于是采用垃圾回收,显然任意时刻元素数\(\leq|[0,n]|=n+1\)。
时间复杂度\(\mathrm O(n\log n)\),空间复杂度\(\mathrm O(n)\)。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
const int inf=0x3f3f3f3f;
mt19937 rng(20060617/*信仰优化*/);
const int N=300000;
int n;//数的数量
int l[N+1],r[N+1];//限制区间
struct fhq_treap{//fhq-Treap
int sz/*点数*/,root/*根*/;
struct node{unsigned key;int lson,rson,sz,v,lz;}nd[N+2];
#define key(p) nd[p].key
#define lson(p) nd[p].lson
#define rson(p) nd[p].rson
#define sz(p) nd[p].sz
#define v(p) nd[p].v
#define lz(p) nd[p].lz
stack<int> rub;//垃圾栈
void recyc(int p){rub.push(p);}//垃圾回收
int nwnd(int v){//新建节点
int p;
if(rub.size())p=rub.top(),rub.pop();//垃圾再利用
else p=++sz;
return nd[p]=node({rng(),0,0,1,v,0}),p;
}
void sprup(int p){sz(p)=sz(lson(p))+1+sz(rson(p));}//上传
void sprdwn(int p){//下传
if(lz(p)){
v(lson(p))+=lz(p);v(rson(p))+=lz(p);
lz(lson(p))+=lz(p);lz(rson(p))+=lz(p);
lz(p)=0;
}
}
int bld(int l=0,int r=n){//建树
int mid=l+r>>1,p=nwnd(mid?inf:0);
if(l<=mid-1)lson(p)=bld(l,mid-1);
if(mid+1<=r)rson(p)=bld(mid+1,r);
return sprup(p),p;
}
void init(){//fhq-Treap初始化
nd[sz=0]=node({0,0,0,0,0,0});
root=bld();
}
pair<int,int> split(int x,int p=-1){~p||(p=root);
if(!x)return mp(0,p);
pair<int,int> sp;
sprdwn(p);
if(x<=sz(lson(p)))return sp=split(x,lson(p)),lson(p)=sp.Y,sprup(p),mp(sp.X,p);
return sp=split(x-sz(lson(p))-1,rson(p)),rson(p)=sp.X,sprup(p),mp(p,sp.Y);
}
int mrg(int p,int q){
if(!p||!q)return p|q;
sprdwn(p);sprdwn(q);
if(key(p)<key(q))return rson(p)=mrg(rson(p),q),sprup(p),p;
return lson(q)=mrg(p,lson(q)),sprup(q),q;
}
int lss(int v,int p=-1)/*以p为根的子树内<v的数的数量*/{~p||(p=root);
if(!p)return 0;
sprdwn(p);
if(v(p)<v)return sz(lson(p))+1+lss(v,rson(p));
return lss(v,lson(p));
}
void trs(int x){//转移
pair<int,int> sp=split(lss(l[x]));
if(!sp.Y)return;
pair<int,int> sp0=split(lss(r[x],sp.Y),sp.Y);
if(sp0.Y){
pair<int,int> sp1=split(1,sp0.Y);
recyc(sp1.X);//删除并回收
v(sp0.X)++;lz(sp0.X)++;//区间增加
root=mrg(mrg(mrg(sp.X,nwnd(l[x])/*添加*/),sp0.X),sp1.Y);
}
else{
pair<int,int> sp1=split(sz(sp0.X)-1,sp0.X);
recyc(sp1.Y);//删除并回收
v(sp1.X)++;lz(sp1.X)++;//区间增加
root=mrg(mrg(sp.X,nwnd(l[x])/*添加*/),sp1.X);
}
}
int at(int x,int p=-1)/*第x个元素(0-indexed)*/{~p||(p=root);
sprdwn(p);
if(sz(lson(p))+1==x+1)return v(p);
if(sz(lson(p))>=x+1)return at(x,lson(p));
return at(x-sz(lson(p))-1,rson(p));
}
void dfs(int p=-1)/*调试用*/{~p||(p=root);
if(!p)return;
sprdwn(p);
dfs(lson(p));
cout<<v(p)<<" ";
dfs(rson(p));
}
}dp;//当前的DP数组
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>l[i]>>r[i];
dp.init();//fhq-Treap初始化
for(int i=1;i<=n;i++)dp.trs(i)/*,dp.dfs(),puts("")*/;//转移
// for(int i=0;i<=n;i++)cout<<dp.at(i)<<" ";puts("");
for(int i=n;;i--)if(dp.at(i)<inf)return cout<<i,0;//计算答案
return 0;
}