三维偏序 CDQ

来源:互联网 发布:张宴 nginx 编辑:程序博客网 时间:2024/05/01 02:23

来看这样一道题

(bzoj陌上百合)

描述:
给出 n 个点(x,y,z),请找出最长上升子序列,即对于选择序列中的 i<j,
xi<=xj,yi<=yj,zi<=zj。
输出最长上升子序列的长度和方案数。
输入:
第一行包含一个整数 n
接下来 n 行,每行有 3 个整数 xi,yi,zi
输出:
输出长度和方案数(方案数对2^30取mod)
输入输出样例:
cdq.in cdq.out
3
2 0 0
0 1 1
0 1 0
2 1
5
3 0 0
0 1 0
0 0 1
0 2 2
3 3 3
3 2
数据范围:
x,y,z<=1000000000
对于 20%数据 n<=1000
对于 100%数据 n<=100000


这就是经典的3维偏序.

三维偏序是可以对点排序之后求的


首先看二维偏序:(x,y)

如果我们对x排序,那么就转化成求y的lis


同理,我们也需要一些转化.

按照cdq分治基本思想,一维排序,二维分治,三维树状数组(数据结构)


-----------------------------------------------------------------------------------------------

接下来就以这道题为例,浅谈cdq分治思想

假设我们按第一维的坐标排好序了,我们就需要在x排好序的点对(y,z)中找二维偏序.

因为之前已经排过序(x)了,所以直接再排序是有一点问题的,在这里可以写树套树维护.

树套树的码量有点大,而且不太好写,所以就不谈了。


谈点实际的:

我们已经对x这一维排好序了,那么我们将[l,r]的点对分成两个区间[l,mid] 和 [mid+1,r]

然后依次递归调用,先处理[l,mid]的信息,然后处理[l,mid]对[mid+1,r]的影响,最后处理[mid+1,r]的信息

(为什么要这样?因为这道题是求三维lis,左边的区间会对右边的产生影响.)

继续刚才的分治:如果我们得到了两个区间的信息,就可以对第3维维护信息了。


可能有些不懂,下面讲一下具体的吧:

(下面是计算[l,mid]对[mid+1,r]的影响)

我们将2分的区间分别按y排序(因为第一维之前排过序,可以保证[mid+1,r]的所有x>=[l,mid]的x. 因为是考虑左区间对右区间的影响,所以就不用考虑区间内部信息)

而后第二位也是有序了的。

然后枚举[mid+1,r]每个元素,并且维护左区间的一个指针i,一旦当前枚举到的元素j的y,就把左区间所有i.y<=j.y的信息更新到树状数组里面去(我们保证了y的单调性,所以复杂度大致是 区间长*更新单次的时间)


树状数组用来干什么?我们枚举到右区间一个j,将左区间中所有i.y<=j.y(第一次排序保证了i.x<=j.x)的z在树状数组对应的位置的dp值更新为max(dp[i]).            所以树状数组是用来维护第3维的dp值

对于枚举到的j,更新了所有i.y<=j.y的i的信息之后dp[j]=query([1,j.z])(这一段区间的最大dp值)+1;


然后把所有dp值求max就得到了答案1


那么怎么求方案呢?

我其实也很迷茫,不过参考了各种资料后,发现只需要再维护一个数组保存方案数即可.

在更新dp值的时候,if(dp[j]<getmax(1,j.z).dp)dp[j]=getmax(1,j.z).dp,更新方案数cnt[j]=getmax(1,j.z).cnt;

                              if(dp[j]==getmax(1,j.z).dp)cnt[j]+=getmax(1,j.z).cnt;


就行了!

有不清楚的,再细想一下吧!


AC代码:

<span style="font-size:18px;">#include<cstdio>#include<algorithm>#include<queue>#include<cstring>#include<cstdlib>#include<iostream>#include<cmath>#include<ctime>#define LL long longconst LL M=1<<30;using namespace std;double st,ft;const int maxn=100000+20;struct xnode{int x,y,z,id;}q[maxn],tl[maxn],tr[maxn];int dp[maxn];LL c[maxn];int n;bool cmpx(xnode a,xnode b){if(a.x!=b.x)return a.x<b.x;if(a.y!=b.y)return a.y<b.y;return a.z<b.z;}bool cmpy(xnode a,xnode b){if(a.y!=b.y)return a.y<b.y;return a.z<b.z;}int det[maxn];int m;int cnt;int find(int x){return lower_bound(det+1,det+m+1,x)-det;}struct Ans{int dp;LL cnt;Ans(){dp=cnt=0;}};Ans f[maxn];void updata(int i,int x,LL v){while(i<=m){if(f[i].dp<x){f[i].dp=x;f[i].cnt=v%M;}else if(f[i].dp==x){f[i].cnt+=v;f[i].cnt%=M;}i+=(i&-i);}}void clear(int i){while(i<=m){f[i].dp=0;f[i].cnt=0;i+=(i&-i);}}Ans query(int i){Ans ans;while(i){if(ans.dp<f[i].dp){ans.dp=f[i].dp;ans.cnt=f[i].cnt%M;}else if(ans.dp==f[i].dp){ans.cnt+=f[i].cnt;ans.cnt%=M;}i-=(i&-i);}return ans;}void work(int l,int r){int mid=(l+r)>>1;int t1,t2;t1=t2=0;for(int i=l;i<=mid;i++)tl[++t1]=q[i];for(int i=mid+1;i<=r;i++)tr[++t2]=q[i];sort(tl+1,tl+t1+1,cmpy);sort(tr+1,tr+t2+1,cmpy);for(int i=1,j=1;j<=t2;j++){while(tl[i].y<=tr[j].y&&i<=t1){updata(tl[i].z,dp[tl[i].id],c[tl[i].id]);i++;}Ans sum=query(tr[j].z);sum.dp++;if(dp[tr[j].id]<sum.dp){dp[tr[j].id]=sum.dp;c[tr[j].id]=sum.cnt%M;}else if(dp[tr[j].id]==sum.dp){c[tr[j].id]+=sum.cnt;c[tr[j].id]%=M;}}for(int i=l;i<=mid;i++)clear(q[i].z);}void cal(int l,int r){if(l==r)return ;int mid=(l+r)>>1;cal(l,mid);work(l,r);cal(mid+1,r);}int main(){freopen("cdq.in","r",stdin);freopen("cdq.out","w",stdout);cnt=0;scanf("%d",&n);for(int i=1;i<=n;i++){q[i].id=i;dp[i]=1;c[i]=1;}for(int i=1;i<=n;i++){scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);det[++cnt]=q[i].z;}sort(det+1,det+cnt+1);m=unique(det+1,det+cnt+1)-det-1;for(int i=1;i<=n;i++)q[i].z=find(q[i].z);sort(q+1,q+n+1,cmpx);cal(1,n);int ans=0;LL sum=0;for(int i=1;i<=n;i++){if(dp[i]>ans){ans=dp[i];sum=c[i]%M;}else if(dp[i]==ans){sum+=c[i];sum%=M;}}printf("%d %I64d\n",ans,sum);return 0;}</span>


2 0
原创粉丝点击