再次感谢这位大佬的博客:https://www.cnblogs.com/ljc20020730/p/10395866.html

CDQ分治,是一种在分治合并中计算前面值对后面答案的贡献的一种算法。今天主要围绕多维偏序问题来对CDQ分治进行介绍

先定义偏序:(以下转载自百度百科)

设R是集合A上的一个关系,如果R是自反的、反对称的和可传递的,则称R是集合A的偏序关系,简称偏序

(1)自反性:a≤a,∀a∈P;
(2)反对称性:∀a,b∈P,若a≤b且b≤a,则a=b;
(3)传递性:∀a,b,c∈P,若a≤b且b≤c,则a≤c;

二维偏序:给定n个二元组,求有多少对二元组满足$a[i].x>=a[j].x$,且$a[i].y>=a[j].y$

暴力$n^{2}$肯定不行,我们可以采用归并排序的方法,对第一维从小到大进行排序,这样只会前面影响后面,然后我们再用类似于“逆序对”的方法统计第二就可以啦~~

拓展题:CF957E(怎么用二维偏序自己想哦~)

三位偏序:和二维偏序类似,在用逆序对维护第二维的同时,我们需要再用数据结构统计一下同时满足第三维时的贡献

做法:

1.对第一维进行排序

2.对二维进行归并排序,并同时统计前面对后面的贡献(在同一组的之前已经统计过了):

3.若前面$a[i].y$一直小于等于$a[j].y$,则将$a[i].z$塞到树状数组里维护:即这个这段区间内值为$z$的有几个(记得对于$z$离散化)

4.一旦$a[i]>a[j]$就说明后面的$a[i]$不会再对$a[j]$产生贡献,所以对于$a[j].z$在树状数组$[1,a[j].z]$区间内查询有多少个$z$即为贡献

代码实现:(细节超多啊QAQ)

首先是要去重,因为完全相同的三元组是会互相影响的,而在归并排序中只会左边影响右边,会少讨论情况

其次是在统计答案时每几个完全相同的三元组的内部影响要加上

还有就是在树状数组用完以后要清0,但不要用memset,不然复杂度会直线上升,只需要对于之前更新过的树状数组再反着更新回来就可以啦~

模板题:陌上花开

#include<bits/stdc++.h>
using namespace std;
const int N=;
void print()
{
puts("OK");
} int n,tt[N],ans[N],f[N],k;
int C[N*];
struct node{
int x,y,z,id,cnt;
bool operator < (const node &rhs) const{
if(x!=rhs.x) return x<rhs.x;
if(y!=rhs.y) return y<rhs.y;
if(z!=rhs.z) return z<rhs.z;
return id<rhs.id;
}
bool operator == (const node &rhs) const{
return x==rhs.x&&y==rhs.y&&z==rhs.z;
}
}a[N];
node t[N]; int tot=; int lowbit(int x)
{
return x&(-x);
} void update(int p,int x)
{
while(p<=k)//树状数组长度为k(值不同的个数)
{
C[p]+=x;
p+=lowbit(p);
}
} int query(int x)
{
int ret=;
while(x>)
{
ret+=C[x];
x-=lowbit(x);
}
return ret;
} void init()
{
sort(a+,a+n+);
int i=; tot=;
while(i<=n)
{
int j=i+;
while(j<=n&&a[j]==a[i]) j++;
t[++tot]=a[i];
t[tot].cnt=j-i;
t[tot].id=tot;
i=j;
}
for(int i=;i<=tot;i++) a[i]=t[i];
for(int i=;i<=tot;i++) tt[i]=a[i].z;
sort(tt+,tt+tot+);
int m=unique(tt+,tt+tot+)-tt-;
for(int i=;i<=tot;i++)
{
a[i].z=lower_bound(tt+,tt+m+,a[i].z)-tt;
//记得要算自己内部的影响
}
} void CDQ(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>;
CDQ(l,mid);
CDQ(mid+,r);
int i=l,j=mid+,top=l-;
while(i<=mid&&j<=r)
{
if(a[i].y<=a[j].y)//能做出贡献
{
update(a[i].z,a[i].cnt);
t[++top]=a[i++];
}
else
{
ans[a[j].id]+=query(a[j].z);//之前j写成i了
t[++top]=a[j++];
}
}
while(i<=mid)
{
update(a[i].z,a[i].cnt);
t[++top]=a[i];
i++;
}
while(j<=r)
{
ans[a[j].id]+=query(a[j].z);
t[++top]=a[j];
j++;
}
//for(int i=1;i<=tot;i++) cout<<ans[i]<<" "; cout<<endl;
for(int i=l;i<=mid;i++)
{
update(a[i].z,-a[i].cnt);
}
for(int i=l;i<=r;i++) a[i]=t[i];
} int main()
{
memset(ans,,sizeof(ans));
memset(f,,sizeof(f));
scanf("%d%d",&n,&k);
for(int i=;i<=n;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
a[i].cnt=;
}
init();
CDQ(,tot);
for(int i=;i<=tot;i++)
{
ans[a[i].id]+=a[i].cnt-;//之前枚举ans的坐标,却发现没有保存原坐标对应现坐标,但是现a[i]有对应原坐标,所以枚举a下标
f[ans[a[i].id]]+=a[i].cnt;//现顺序已经被归并排序弄得毫无意义,要用id,加cnt,而不是1
}
for(int i=;i<n;i++) printf("%d\n",f[i]);
return ;
}

四维偏序:这是神仙题啊~~~一般用bitset解决,CDQ套CDQ复杂度实在是太大了,但还是介绍一下思路吧

首先第一维还是排序,然后对第二位进行CDQ分治,但是还剩下两维(可以每一次插入都把后三位拉出来进行cdq,但复杂度太高了,我们考虑在最后进行)

因为只有前面对后面有影响,所以我们记录它是在左边还是在右边

显然在归并排序之后第二维已经达到了有序,然后我们再来一遍CDQ,对记录是左边的进行update,右边的进行query,而且因为从第二维小到大排序,所以一定只有前面影响后面

最后就是再对第三位进行分治,然后对第四维进行树状数组统计就可以啦~~

(代码详见上述那位神仙的博客,本蒟蒻没有这个实力啊QAQ)

学习笔记——CDQ分治的更多相关文章

  1. 学习笔记 | CDQ分治

    目录 前言 啥是CDQ啊(它的基本思想) 例题 后记 参考博文 前言 博主太菜了 学习快一年的OI了 好像没有什么会的算法 更寒碜的是 学一样还不精一样TAT 如有什么错误请各位路过的大佬指出啊感谢! ...

  2. [学习笔记] CDQ分治 从感性理解到彻底晕菜

    最近学了一种叫做CDQ分治的东西...用于离线处理一系列操作与查询似乎跑得很快233 CDQ的名称似乎源于金牌选手陈丹琦 概述: 对于一坨操作和询问,分成两半,单独处理左半边和处理左半边对于右半边的影 ...

  3. [学习笔记]CDQ分治和整体二分

    序言 \(CDQ\) 分治和整体二分都是基于分治的思想,把复杂的问题拆分成许多可以简单求的解子问题.但是这两种算法必须离线处理,不能解决一些强制在线的题目.不过如果题目允许离线的话,这两种算法能把在线 ...

  4. [学习笔记] CDQ分治&整体二分

    突然诈尸.png 这两个东西好像都是离线骗分大法... 不过其实这两个东西并不是一样的... 虽然代码长得比较像 CDQ分治 基本思想 其实CDQ分治的基本思想挺简单的... 大概思路就是长这样的: ...

  5. 算法笔记--CDQ分治 && 整体二分

    参考:https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen 前置技能:树 ...

  6. 一篇自己都看不懂的CDQ分治&整体二分学习笔记

    作为一个永不咕咕咕的博主,我来更笔记辣qaq CDQ分治 CDQ分治的思想还是比较简单的.它的基本流程是: \(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列.显然,会影响查询操作 ...

  7. 【教程】简易CDQ分治教程&学习笔记

    前言 辣鸡蒟蒻__stdcall终于会CDQ分治啦!       CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. CDQ分治的基 ...

  8. [偏序关系与CDQ分治]【学习笔记】

    组合数学真是太棒了 $CDQ$真是太棒了(雾 参考资料: 1.<组合数学> 2.论文 课件 很容易查到 3.sro __stdcall 偏序关系 关系: 集合$X$上的关系是$X$与$X$ ...

  9. 初学cdq分治学习笔记(可能有第二次的学习笔记)

    前言骚话 本人蒟蒻,一开始看到模板题就非常的懵逼,链接,学到后面就越来越清楚了. 吐槽,cdq,超短裙分治....(尴尬) 正片开始 思想 和普通的分治,还是分而治之,但是有一点不一样的是一般的分治在 ...

随机推荐

  1. vue笔记一:设置表头的背景颜色以及下边框颜色

    最近做的一个项目有个小小的需求,就是把表头的背景颜色改为黑色,表格边框颜色改为这个颜色,中间出了些问题,因为vue的表格样式是内联样式,一修改就影响到其他地方的表格样式,尽管我单独加了id还是不行,经 ...

  2. 其它课程中的python---6、python读取数据

    其它课程中的python---6.python读取数据 一.总结 一句话总结: 记常用和特例:慢慢慢慢的就熟了,不用太着急,慢慢来 库的使用都很简单:就是库的常用函数就这几个,后面用的时候学都来得及. ...

  3. 四:RF框架appium工具之xpath定位

    XPATH定位方法具体的学会,还是在今年1月份,以前运用的都不熟练. 这个定位神器是一定要掌握的,不然有你抓狂的时候. 第一要掌握它的书写格式,这个好上手. 第二要掌握它的具体用法.这个就要多练习了, ...

  4. java.io.IOException: Could not find resource SqlMapConfig.xml

    java.io.IOException: Could not find resource SqlMapConfig.xml 创建mybatis工程时遇到的问题 问题的来源:当我们在项目中和src同级的 ...

  5. Codeforces 1191A Tokitsukaze and Enhancement

    题目链接:http://codeforces.com/problemset/problem/1191/A 思路:枚举 16 种情况输出最高的就行. AC代码: #include<bits/std ...

  6. python 出现indentationError:expected an indented block!

    出现这个问题,代码一般是没问题的,剩下你要考虑: 1. 缩进对齐是否有问题 2. python脚本的格式是啥,如果你在linux上运行,编码需要是unix;  (大部分情况下,我们是在windows下 ...

  7. shell默认参数脚本

    如果不加参数则默认执行restart函数,加stop,则执行stop函数 #!/bin/bash # version function stop(){ # stop the command } fun ...

  8. C# WinFrom 关于MDI

    dev是一个牛B 到没边的控件 我们正常用winform做个原始mdi窗体 一点都不好看 但 用的dev只需要一个控件 就可让显示舒服多了 建一个项目 上边放一个 xtraTabbedMdiManag ...

  9. python调用tushare获取上市公司管理层人员薪酬和持股

    接口:stk_rewards 描述:获取上市公司管理层薪酬和持股 积分:用户需要2000积分才可以调取,具体请参阅本文最下方积分获取办法 注:tushare包下载和初始化教程,请查阅我之前的文章 输入 ...

  10. 47-Ubuntu-系统信息-2-df和du查看磁盘和目录空间占用

    序号 命令 作用 01 df -h disk free 显示磁盘剩余空间;-h以人性化的方式显示文件大小 02 du -h [目录名] disk usage 显示目录下的文件大小 注:显示磁盘信息的时 ...