三维偏序 CDQ
2016-02-21 10:52
218 查看
来看这样一道题
(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代码:
(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 long const 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>
相关文章推荐
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- 三维漫游飞行路径建立方法与插值算法
- 十九个国内外主流的三维GIS
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- 对分治算法的几点思考
- DP问题各种模型的状态转移方程
- POJ-1695-Magazine Delivery-dp
- nyoj-1216-整理图书-dp
- TYVJ1193 括号序列解题报告
- 对DP的一点感想
- TYVJ上一些DP的解题报告
- soj1005. Roll Playing Games