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\)种,于是分情况讨论:

  1. 转移到\(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\)
  2. 转移到\(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]\)
  3. 转移到\(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;
}
posted @ 2020-04-25 14:34  ycx060617  阅读(315)  评论(0编辑  收藏  举报