前言

上一篇文章已经介绍了简单的CDQ分治,包括经典的二维偏序和三维偏序问题,还有带修改和查询的二维/三维偏序问题。本文讲介绍多重CDQ分治的嵌套,即多维偏序问题。

四维偏序问题

      给定N(N<=20000)个有序四元组(a,b,c,d),求对于每一个四元组(a,b,c,d),有多少个四元组(a2,b2,c2,d2)满足a2<a && b2<b && c2<c && d2<d

       不需要太多思考,就能得到一个O(nlog^3n)算法:先按照a元素排序,然后进行CDQ分治,在合并的时候按照b元素的升序对两个子问题进行合并(对这个操作,我们暂且称为“对a分治,按照b合并”)。这样,我们需要一个能进行以下操作的数据结构:

  1. 插入一个二元组(c,d);
  2. 给出一个二元组(c,d),询问有多少个二元组(c2,d2)满足c2<c && d2<d。

这个问题很容易用树套树解决,但是树套树巨大的常数和空间消耗往往是性能的瓶颈。记得我们之前说过,CDQ分治能代替复杂的数据结构,并将问题“降维”。这里,我们就用双重嵌套的CDQ分治,把这个问题继续降维,避免使用树套树。

回忆二维偏序问题,我们对第一维分治之后,所有的二元组被我们划分为了左右两部分,左边和右边各自的内部问题已经通过递归解决,剩下要考虑的就是左边的修改对右边的查询的影响。我们不妨把分治后的二元组重新标记一下,左边的为(L,b),右边的为(R,b)。这时候,(a1,b1)对(a2,b2)有影响,当且仅当a1 == L && a2 == R && b1 < b2。然后我们按照b的顺序合并,解决了这一问题。

对于三维偏序问题也是一样的,对第一维分治并且重新标记之后,只有(L,b1,c1)可能对(R,b2,c2)有影响。我们用“按顺序归并”保证b元素的顺序,用树状数组保证c元素的顺序。

对于四维偏序问题,我们也按照这样的思路进行下去。对第一维分治,并把所有元素重新标记为(L,b,c,d)和(R,b,c,d),然后按照b的顺序合并。注意,我们在这里只是做合并,并不用任何数据结构对c和d加以维护。

合并完之后,我们得到了一个按照b值升序排列的序列,现在,我们把这个序列复制一份,用CDQ分治统计刚刚我们没有统计的信息——左边的修改对右边的查询的影响

这时候这个序列仅仅是b值有序,但是a值是杂乱无章的,不过我们之前已经对a值进行了重新标记,现在a值只可能是L或者R。

我们对b值进行分治,递归处理左右两边的子问题(别忘了我们现在要处理的问题是“在第一维分治之后,左边的修改对右边的查询的影响”)。然后,把所有b值也重新标号为L和R,于是我们得到了这样一个序列(L/R,L/R,c,d)。注意,现在只有(L,L,c,d)可能对(R,R,c,d)产生影响!请读者仔细考虑这个条件,这是理解多重CDQ分治的关键!

然后我们按照c值从小到大进行合并,这保证了统计时c值的顺序,同时用树状数组维护d值的信息,保证考虑到d值的顺序。只有一个元素为(L,L,c,d)的时候,它才可能影响到后面的查询;只有一个元素为(R,R,c,d)的时候,它才可能收到前面的修改的影响。即,我们在归并的时候,把一个d值加入树状数组,当且仅当这个四元组的a == L && b == L;我们向树状数组查询d值的信息并应用到这个查询上面,当且仅当这个四元组的a == R && b == R。

让我们总结一下全过程:

  1. 对第一维进行排序。
  2. 对第一维重新标号,然后对第一维分治,递归解决子问题,按照第二维的顺序合并。此时只是单纯的合并,并不进行统计。
  3. 把合并后的序列复制一份,并对第二维重新标号,在复制的那一份中进行CDQ分治。即对第二维分治,递归解决子问题,按照第三维的顺序合并。合并过程中用树状数组维护第四维的信息。

下面是[HZOI 2016]偏序 COGS 2479的AC代码:

 #include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath> using namespace std;
typedef long long ll;
const int MAXN = ; int n; struct Item {
int d1,d2,d3,d4,part; // 分别表示每一维的数据,part为第一维重标号之后的值
}a[MAXN];
const int LEFT = ;
const int RIGHT = ; namespace BIT { // 树状数组相关
int arr[MAXN];
inline int lowbit( int num ) { return num&(-num); }
void add( int idx ) {
for( ; idx <= n; idx += lowbit(idx) ) arr[idx]++;
}
int query( int idx ) {
int ans = ;
for( ; idx; idx -= lowbit(idx) ) ans += arr[idx];
return ans;
}
void clear( int idx ) {
for( ; idx <= n; idx += lowbit(idx) ) arr[idx] = ;
}
} ll ans = ; Item tmp3d[MAXN];
Item tmp2d[MAXN];
void cdq3d( int L, int R ) { // 对第二维分治,按照第三维合并
if( R-L <= ) return;
int M = (L+R)>>; cdq3d(L,M); cdq3d(M,R);
int p = L, q = M, o = L;
while( p < M && q < R ) { // 因为第二维是“左边全都是L,右边全都是R”,所以略去第二维的标号
if( tmp2d[p].d3 < tmp2d[q].d3 ) {
if( tmp2d[p].part == LEFT ) BIT::add( tmp2d[p].d4 );
tmp3d[o++] = tmp2d[p++];
} else {
if( tmp2d[q].part == RIGHT ) ans += BIT::query( tmp2d[q].d4 );
tmp3d[o++] = tmp2d[q++];
}
}
while( p < M ) tmp3d[o++] = tmp2d[p++];
while( q < R ) {
if( tmp2d[q].part == RIGHT ) ans += BIT::query( tmp2d[q].d4 );
tmp3d[o++] = tmp2d[q++];
}
for( int i = L; i < R; ++i ) { // 清空树状数组
if( tmp3d[i].part == LEFT ) BIT::clear( tmp3d[i].d4 );
tmp2d[i] = tmp3d[i];
}
}
void cdq2d( int L, int R ) { // 对第一维分治,按照第二维合并
if( R-L <= ) return;
int M = (L+R)>>; cdq2d(L,M); cdq2d(M,R);
int p = L, q = M, o = L;
while( p < M && q < R ) {
if( a[p].d2 < a[q].d2 ) {
a[p].part = LEFT; // 重标号
tmp2d[o++] = a[p++];
} else {
a[q].part = RIGHT;
tmp2d[o++] = a[q++];
}
}
while( p < M ) {
a[p].part = LEFT;
tmp2d[o++] = a[p++];
}
while( q < R ) {
a[q].part = RIGHT;
tmp2d[o++] = a[q++];
}
for( int i = L; i < R; ++i ) a[i] = tmp2d[i]; // tmp2d为“复制的那一份”
cdq3d(L,R);
} int main() {
freopen( "partial_order.in", "r", stdin );
freopen( "partial_order.out", "w", stdout );
scanf( "%d", &n );
for( int i = ; i < n; ++i ) {
a[i].d1 = i;
scanf( "%d", &a[i].d2 );
}
for( int i = ; i < n; ++i ) scanf( "%d", &a[i].d3 );
for( int i = ; i < n; ++i ) scanf( "%d", &a[i].d4 );
cdq2d(,n); printf( "%lld\n", ans );
return ;
}

习题

[HZOI 2016]偏序 COGS 2479

【教程】CDQ套CDQ——四维偏序问题的更多相关文章

  1. cogs2479 偏序(CDQ套CDQ)

    题目链接 思路 四维偏序 \(CDQ\)套\(CDQ\),第一维默认有序.第二维用第一个\(CDQ\)变成有序的.并且对每个点标记上第一维属于左边还是右边.第二个\(CDQ\)处理第三维,注意两个\( ...

  2. [HZOI 2016] 偏序(CDQ套CDQ)

    传送门 思路: 就是cdq套cdq的模板题 #include <bits/stdc++.h> using namespace std; typedef long long ll; cons ...

  3. HDU - 5126: stars (求立方体内点数 CDQ套CDQ)

    题意:现在给定空空的三维平面,有加点操作和询问立方体点数. 思路:考虑CDQ套CDQ.复杂度是O(NlogN*logN*logN),可以过此题. 具体的,这是一个四维偏序问题,4维分别是(times, ...

  4. HDU5126---stars (CDQ套CDQ套 树状数组)

    题意:Q次操作,三维空间内 每个星星对应一个坐标,查询以(x1,y1,z1) (x2,y2,z2)为左下顶点 .右上顶点的立方体内的星星的个数. 注意Q的范围为50000,显然离散化之后用三维BIT会 ...

  5. 四维偏序 CDQ套CDQ

    对CDQ深一步的理解 昨天做了一道CDQ,看了一堆CDQ可做的题,今天又做了一道四维偏序 感觉对CDQ的理解又深了一点,故来写一写现在自己对于CDQ的理解 CDQ其实就是实现了这样的一个问题的转化: ...

  6. hdu 5126 stars (四维偏序,离线,CDQ套CDQ套树状数组)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5126 思路:支持离线,那么我们可以用两次CDQ分治使四维降为二维,降成二维后排个序用树状数组维护下就好 ...

  7. COGS 2479. [HZOI 2016] 偏序 (CDQ套CDQ)

    传送门 解题思路 四维偏序问题,模仿三维偏序,第一维排序,第二维CDQ,最后剩下二元组,发现没办法处理,就继续嵌套CDQ分治.首先把二元组的左右两边分别打上不同的标记,因为统计答案时只统计左边对右边的 ...

  8. HDU 5126 stars 4维偏序, CDQ套CDQ

    题目传送门 题意:在一个星空中,按着时间会出现一些点,现在john想知道,在某个时间内有多少个星星是的坐标是满足条件的.(x1<=x<=x2, y1 <= y <= y2, z ...

  9. 【算法学习】【洛谷】cdq分治 & P3810 三维偏序

    cdq是何许人也?请参看这篇:https://wenku.baidu.com/view/3b913556fd0a79563d1e7245.html. 在这篇论文中,cdq提出了对修改/询问型问题(Mo ...

随机推荐

  1. 你究竟有多了解Spring?

    曾经在一本书上看到这么一句话: 一个程序员的能力不在于增加代码的能力,而在于减少代码的能力. 基于以上我认之为真的命题,我经常问和我一起工作的程序员:你的程序还能不能精简一点?如果能,那能不能再精简一 ...

  2. ETL数据从sqlserver到mysql之间迁移

    因近期需要进行sqlserver数据到mysql之间的数据同步.偶然之间发现了这一款工具ELK 一.下载 1.Kettle可以在http://kettle.pentaho.org/网站下载 2.下载的 ...

  3. const 与 readonly知多少

    原文地址: http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 尽管你写了很多年的C#的代码,但是可能当别人问到你cons ...

  4. 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧

    做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...

  5. Python 历遍目录

    Automate the Boring Stuff 学习笔记 01 使用 os 模块的 walk() 函数可以实现历遍目录的操作,该函数接收一个绝对路径字符串作为必选参数,返回三个参数: 当前目录—— ...

  6. activiti工作流的web流程设计器整合视频教程 SSM 和 独立部署

    本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...

  7. 在centos 服务器上安装phalcon框架 undefined symbol: php_pdo_get_dbh_ce

    去git 下载对应版本的框架 命令行: sudo yum install php-devel pcre-devel gcc make 然后使用GIT clone到服务器上,然后 git clone g ...

  8. Android中使用AsyncTask实现文件下载以及进度更新提示

    Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和 ...

  9. 对iOS10新增Api的详细探究

    本文主要是一些对iOS新功能的探索,之前发现博客里关于iOS新功能的分析大多是过于概括,每个功能几句话,无法了解到具体的功能.所以本次的探索是基于Api层面,着重看一些具体用法所做的笔记,本来想分别画 ...

  10. HTML5-电影影评网

    学习完了HTML5的新标签,然后结合之前的案例做了第一个小案例.自我感觉良好.下面我来展示一下图片 这是我浏览其他网站的时候以为发现的新功能可以运行代码,这是运行之后截得图片.自我感觉照片还是蛮高大上 ...