3维偏序

Description

N(N<=40000)个人,每个人有三种能力值Pi,Qi,Ri (1<=Pi,Qi,Ri<=2^31)
如果Pi>Pj && Qi>Qj && Ri>Rj,称IJ有能力
现在要求出最长的一个序列A=(A1,A2,…,At),满足AiAi+1有能力。

Input

第一个数N表示有N个人,
第二到N+1行每行三个整数,表示第i人的能力值Pi,Qi,Ri 

Output

输出最长的序列的长度

Hint

40%数据N<=2000
100%
数据N<=40000

介绍一种优秀的算法,CDQ分治(顶一层数据结构啊还不优秀吗?)。

首先使用该算法必须满足离线,以上面这一道题为例子,如果:

1维:直接输出长度

2维:按一位排序,dp:f[i]=max{f[j]+1}

那么3维怎么办呢?还是先排序降一维,这个时候,数据结构大佬就可以用树套树之类的随便乱搞了……不过对于蒟蒻来说,代码量巨大,而且难调。这个时候,陈丹琪dalao提出了利用分治思想的c(chen)d(dan)q(qi)算法。类比一下归并排序求逆序对,我们发现,二者是类似的。那么对于此题,我们就得到了如下算法流程:

1、按某一维排序(降维)
2、计算左边的贡献,计算中间的贡献,计算右边的贡献(有时左右中更好写,但前提是不相互影响) 
3、更新答案 

#include<bits/stdc++.h>
using namespace std;
const int N = 4e4+10;
#define Inc(i,L,R) for(register int i=(L);i<=(R);++i)
struct P{
	int x,y,z,f;
	inline void read(){
		scanf("%d%d%d",&x,&y,&z),f=1;
	}
};
inline bool cmpx(const P&A,const P&b){
	if(A.x==b.x&&A.y==b.y)return A.z<b.z;
	if(A.x==b.x)return A.y<b.y;
	return A.x<b.x;
}
inline bool cmpy(const P&A,const P&b){
	if(A.x==b.x&&A.y==b.y)return A.z<b.z;
	if(A.y==b.y)return A.x<b.x;
	return A.y<b.y;
}
struct Seg_Tree{
	int n,cnt,ls[N<<1],rs[N<<1],maxx[N<<1];
	inline void init(int _n){
		this->n=_n;
		memset(ls,0,sizeof(ls));
		memset(rs,0,sizeof(rs));
		memset(maxx,0,sizeof(maxx));
	}
	#define ls ls[v]
	#define rs rs[v]
	inline void pushup(int v){maxx[v]=max(maxx[ls],maxx[rs]);}
	inline void insert(int &v,int L,int r,int pos,int val){
		if(L>pos||r<pos)return ;
		if(!v)v=++cnt;
		if(L==pos&&pos==r)return maxx[v]=(val==-1?0:max(val,maxx[v])),void();
		int Mid=L+r>>1;
		insert(ls,L,Mid,pos,val),insert(rs,Mid+1,r,pos,val);
		pushup(v); 
	}
	inline int query(int v,int L,int r,int A,int B){//求最大值 
		if(L>B||r<A)return -(1<<30);
		if(A<=L&&r<=B)return maxx[v];
		int Mid=L+r>>1;
		return max(query(ls,L,Mid,A,B),query(rs,Mid+1,r,A,B));
	}
}tr;
struct CDQ{
	int n,rt;
	P a[N];
	inline void init(){
		scanf("%d",&n);
		Inc(i,1,n)a[i].read();
	}
	inline void disc(){
		int tmp[N];
		Inc(i,1,n)tmp[i]=a[i].z;
		sort(tmp+1,tmp+1+n);
		int len=unique(tmp+1,tmp+1+n)-tmp-1;
		Inc(i,1,n)a[i].z=lower_bound(tmp+1,tmp+1+len,a[i].z)-tmp;
	}
	inline void merge(int L,int r){
		if(L>=r)return ;
		int Mid=L+r>>1;
		merge(L,Mid);//先处理左边
		sort(a+L,a+Mid+1,cmpy),sort(a+Mid+1,a+r+1,cmpy);
		int i=L,j=Mid+1;
		while(j<=r){
			while(i<=Mid&&a[i].y<=a[j].y)tr.insert(rt,1,n,a[i].z,a[i].f),++i;
			a[j].f=max(a[j].f,tr.query(rt,1,n,1,a[j].z)+1),++j;//f[i]=max{f[j]+1} 
		}
		while(--i>=L)tr.insert(rt,1,n,a[i].z,-1);//注意这里要清空贡献
		sort(a+Mid+1,a+r+1,cmpx);
		merge(Mid+1,r);
	}
	inline void solv(){
		sort(a+1,a+1+n,cmpx);
		merge(1,n);//按x排序,降一维
		int ans=0;
		Inc(i,1,n)ans=max(ans,a[i].f);
		cout<<ans<<"\n";
	}
}cdq;
int main(){
	cdq.init();
	cdq.disc();
	cdq.solv();
	return 0;
}

值得注意的是,上述代码只能解决x,y,z互不相同的问题,对于有可能重复出现的值,需要进一步的优化。

(话说回来,我也只会用cdq搞个三维偏序玩/(ㄒoㄒ)/~~)

%%%

猜你喜欢

转载自blog.csdn.net/DancingZ/article/details/81289482