• CodeForces 809D


    洛谷题目页面传送门 & CodeForces题目页面传送门

    题意见洛谷里的翻译。

    一脸DP的样,于是考虑DP。

    与普通的LIS类似,设(dp_{i,j})表示考虑到第(i)个数,最后一位为(a_i=j)的LIS长度。显然,第二维是定义在值域(left[1,10^9 ight])上的,无论如何优化它都会出现在时间/空间复杂度上,受不起。于是考虑一种惯用套路,将DP数组的定义域和值域调换,即设(dp_{i,j})表示考虑到第(i)个数,长度为(j)的IS的最后一位最小为多少(如果无法实现则为(+infty))。

    边界为(dp_{0,i}=egin{cases}0&i=0\+infty&i>0end{cases}),目标为(maxlimits_{iin[1,n],dp_{n,i}<+infty}{i}),状态转移方程为(dp_{i,j}=minegin{cases}dp_{i-1,j}\max(l_i,dp_{i-1,j-1}+1)&dp_{i-1,j-1}<rend{cases})(显然,基于贪心,若选(a_i),那么在可能情况下(a_i)越小越好)。这样空间复杂度可以用滚动数组压到(mathrm O(n)),但时间复杂度是(mathrm O!left(n^2 ight))的。考虑优化。

    注意到转移时的最优决策只可能为(dp_{i-1,j},l_i,dp_{i-1,j-1}+1)(3)种,于是分情况讨论:

    1. 转移到(l_i)。当且仅当(egin{cases}l_igeq dp_{i-1,j-1}+1\l_ileq dp_{i-1,j}end{cases})。不难发现,(forall iin[0,n])(dp_i)不为(+infty)的那部分是严格单调上升的(易证)。于是我们设(las)为最大的(j)使得(dp_{i-1,j}<l_i)。那么(egin{cases}l_igeq dp_{i-1,j-1}+1\l_ileq dp_{i-1,j}end{cases}Rightarrowegin{cases}j-1leq las\j>lasend{cases}Rightarrow j=las+1)
    2. 转移到(dp_{i-1,j-1}+1)。当且仅当(egin{cases}dp_{i-1,j-1}+1>l_i\dp_{i-1,j-1}+1leq dp_{i-1,j}\dp_{i-1,j-1}<rend{cases})(dp_{i-1,j-1}+1=l_i)归在情况(1)里)。根据单调性,(dp_{i-1,j-1}+1leq dp_{i-1,j})显然恒成立。设(las')为最大的(j)使得(dp_{i-1,j}<r_i)。那么(egin{cases}dp_{i-1,j-1}+1>l_i\dp_{i-1,j-1}<rend{cases}Rightarrowegin{cases}j-1>las\j-1leq las'end{cases}Rightarrow jin[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(nlog 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;
    }
    
  • 相关阅读:
    深入理解网卡配置bond的意义和作用
    关于B站UP主墨茶official逝世的感想
    Python内置函数学习笔记
    Python学习笔记(4)
    测试工作中不可忽略的点
    Python学习笔记(3)
    Python学习笔记(2)
    Python学习笔记(1)
    docker-compose部署
    Xtrabackup源码安装
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-809D.html
Copyright © 2020-2023  润新知