博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
偏序问题与CDQ分治
阅读量:5324 次
发布时间:2019-06-14

本文共 3015 字,大约阅读时间需要 10 分钟。

NOIP前这么一点点时间,我还是花了一晚上简单学了一下CDQ分治解决偏序问题。

不过暂时来讲,还是只会比较裸一点的,它的精髓还是没有完全掌握。以后有时间会完善吧。。

但是就算只会很裸很裸的,也还是想讲讲。

 

首先二维偏序板子:

a,b两个序列,长度都为n,求有多少个数对(i,j)满足:a[i]<a[j],b[i]<b[j]。(值得注意的是这里的i,j是没规定大小的)

可以看到,这种两个序列被要求满足一定大小关系的问题就是二维偏序问题。。

那么的话,怎么求呢??

可以这么考虑,如果我们只有一维是不是直接求个正序对就行了。

现在有两维的话,我们考虑如果有一个维度已经有序的话,那么就只要考虑第二维满足条件的就好了。

所以就是按第一维排序,第二维正序对就好了,至于归并还是树状数组就随意了。。

代码的话相信强大的您不需要吧 ~O(∩_∩)O~

 

那么三维偏序呢??

可以这么看吧,其实就是在二维基础上,多一个序列,多一个限制条件而已。

那么的话我们还是类似的,第一维排序解决。

还剩下两维怎么搞??

没问题。。我们直接归并排序刚第二维。

考虑合并时,右区间每个数都满足第二维大于左区间,同时第一维也是满足条件,那么

我们只要考虑对于每一个右区间里的数,左区间里有多少满足第三维条件的就好了。。

这样的话我们同样可以建立权值树状数组,维护的是左区间第三维的一个信息,

具体一点就是每从左边来一个数,就在对应位置上+1,右边来一个数,就直接查询比他小的有多少就好了。

至此,三维偏序基本可以很好的解决了。

时间复杂度O(nlog2),空间复杂度O(值域)。

 

但是,如果是值域很大呢?你也许还可以离散化一下。

那如果是n维偏序呢,莫非树套树套树套树。。。??也许对dalao仅仅是复杂点,对我的话就直接GG了。。

 

那么这就可以用到来CDQ分治有效代替高级复杂的数据结构了。

什么是CDQ分治?其实归并排序求逆序对就有CDQ分治的思想。

我的认为大概就是,分别处理子问题后,考虑前一个子问题对后一个子问题造成的影响或产生的贡献,从而得到答案。

就拿三位偏序为例吧,我们用归并套归并,也就是CDQ套CDQ来解决。

同样的,我们只要考虑对于每一个右区间里的数,左区间里有多少满足第三维条件的就好了。

这其实应该是CDQ分治最擅长解决的问题。

我们考虑把每一个数是从左还是右来(第二维排序下),然后在先合并后(保证第二维在之后的归并中不受影响),继续第三维的归并。

那么考虑对于第三维的归并,我们已知一些什么?

最重要的就是我们记录了他在第二维意义下来自左边还是右边。。

那么我们对于每一个第三维排序下,

每从左边来一个数,看他第二维排序是否来自左边,是的话cnt++,

说明它可以对后面的  每一个(第三维排序下)来自右区间,(第二维排序下)来自右区间的数造成1的贡献。

而对于来自右区间的数只要满足上述条件,Ans+=cnt就好了。

然后贴下三维偏序模板(陌上花开)代码吧

#include 
using namespace std;inline int gi () { int x=0, w=0; char ch=0; while (! (ch>='0' && ch<='9') ) { if (ch=='-') w=1; ch=getchar (); } while (ch>='0' && ch<='9') { x= (x<<3) + (x<<1) + (ch^48); ch=getchar (); } return w?-x:x;}const int N=1e5+10;int n,k,d[N],Ans[N];struct Seq { bool Tag; int *Ans; int a, b, c; bool operator == (const Seq &s) const { return a==s.a && b==s.b && c==s.c; }}seq[N],sB[N],sC[N];inline bool CMP (Seq x, Seq y) { return x.a
>1; Merge2 (l, Mid); Merge2 (Mid+1, r); for (int i=l, j=Mid+1, id=l, cnt=0;id<=r;++id) { if ( (j>r || sB[i].c<=sB[j].c) && i<=Mid) sC[id]=sB[i++], cnt+=sC[id].Tag; else { sC[id]=sB[j++]; if (!sC[id].Tag) *sC[id].Ans+=cnt; } } for (int i=l;i<=r;++i) sB[i]=sC[i];}void Merge1 (int l, int r) { if (l==r) return; int Mid= (l+r) >>1; Merge1 (l, Mid); Merge1 (Mid+1, r); for (int i=l, j=Mid+1, id=l;id<=r;++id) { if ( (j>r || seq[i].b<=seq[j].b ) && i<=Mid) sB[id]=seq[i++], sB[id].Tag=1; else sB[id]=seq[j++], sB[id].Tag=0; } for (int i=l;i<=r;++i) seq[i]=sB[i]; Merge2 (l, r);}int main () { n=gi (), k=gi (); for (int i=1;i<=n;++i) { seq[i].a=gi (), seq[i].b=gi (), seq[i].c=gi (); seq[i].Ans=&Ans[i], Ans[i]=0; } sort (seq+1, seq+n+1, CMP); for (int i=n-1;i;--i) if (seq[i]==seq[i+1]) *seq[i].Ans=*seq[i+1].Ans+1; Merge1 (1, n); for (int i=1;i<=n;++i) d[Ans[i]]++; for (int i=0;i

其实可以发现这样的话n维偏序也只要多打几个归并,免去了高维下复杂的数据结构,还是挺好用的。

缺点可能就是比树状数组什么的常数大些吧  还有就是很多拓展问题用CDQ解决只能离线。。

 

大概我就搞了这么点东西吧,东西少,话却说了蛮多。另外的话感谢一下尻哥的友情帮助啦。

 

转载于:https://www.cnblogs.com/Bhllx/p/9815994.html

你可能感兴趣的文章
IOS Layer的使用
查看>>
Android SurfaceView实战 带你玩转flabby bird (上)
查看>>
Android中使用Handler造成内存泄露的分析和解决
查看>>
SSM集成activiti6.0错误集锦(一)
查看>>
个人作业
查看>>
下拉刷新
查看>>
linux的子进程调用exec( )系列函数
查看>>
MSChart的研究
查看>>
C# 索引器
查看>>
MySQLdb & pymsql
查看>>
zju 2744 回文字符 hdu 1544
查看>>
XmlDocument
查看>>
delphi 内嵌汇编例子
查看>>
SQL server 2012 安装SQL2012出现报错: 启用 Windows 功能 NetFx3 时出错
查看>>
【luogu P2298 Mzc和男家丁的游戏】 题解
查看>>
前端笔记-bom
查看>>
MATLAB作图方法与技巧(一)
查看>>
上海淮海中路上苹果旗舰店门口欲砸一台IMAC电脑维权
查看>>
Google透露Android Market恶意程序扫描服务
查看>>
给mysql数据库字段值拼接前缀或后缀。 concat()函数
查看>>