笔记-CDQ 分治

news/2024/11/15 10:29:52/文章来源:https://www.cnblogs.com/z-Sqrt-xf/p/-/CDQ

CDQ 分治

分治,分而治之,一般采取递归的形式,先将要处理的部分分开分别处理,再合并计算。

而 CDQ 分治正是基于分治思想的离线算法。

具体地,CDQ 分治对询问进行分治,对于一个询问区间 \([l,r]\),CDQ 分治进行以下操作:

  1. 处理 \([l,mid]\)
  2. 处理 \([mid+1,r]\)
  3. 计算 \([l,mid]\) 中的修改对 \([mid+1,r]\) 的贡献。

CDQ 分治常用于解决点对相关问题。

对于偏序问题,CDQ 分治可以在较优的时间复杂度内求出,如逆序对等二维偏序。归并排序就是 CDQ 分治思想的一种体现。

接下来进入一道例题。

P3810 【模板】三维偏序(陌上花开)

\(n\) 个元素,第 \(i\) 个元素有 \(a_i,b_i,c_i\) 三个属性,设 \(f(i)\) 表示满足 \(a_j \le a_i\)\(b_j \le b_i\)\(c_j \le c_i\)\(j \ne i\)\(j\) 的数量。

对于 \(d \in [0, n)\),求 \(f(i)=d\) 的数量。

\(1 \le n \le 10^5\)\(1 \le a_i,b_i,c_i \le 2 \times 10^5\)

经典题。

显然这道题是不能 \(O(n^2)\) 暴力枚举的。与点对有关,于是 CDQ 分治。

首先将点按 \(a_i\) 排序,这样就保证只有前面的点对后面的点产生影响。

分治。对于区间 \([l,r]\),假设我们已经处理好了 \([l,mid]\)\([mid+1,r]\),先对这两个区间分别按 \(b_i\) 排序,由于我们在最开始就对整个序列按 \(a_i\) 排序,\([l,mid]\) 中的每个点的 \(a_i\) 一定都是大于 \([mid+1,r]\) 中的每个点的 \(a_i\) 的,所以我们只需要处理 \([l,mid]\)\(b_i\)\(c_i\) 的影响。

将两个区间中的点按 \(b_i\) 从小到大排序(也可用归并实现,文章中使用 sort),然后双指针,\(i\) 代表 \([mid+1,r]\)\(mid+1\) 开始的指针,\(j\) 代表 \([l,mid]\)\(l\) 开始的指针。对于 \(c_i\) 我们可以使用树状数组维护。

对于一个 \(i\),我们将所有 \(b_j \le b_i\) 的点加入树状数组,此时所有加入了树状数组的点 \(x\) 都满足 \(a_x \le a_i,b_x \le b_i\),因此在树状数组上查询 \(c_x \le c_i\) 的点,即 query(c[i]) 即可。

注意清空树状数组。

代码:

#include<bits/stdc++.h> 
using namespace std;
const int N=1e5+5,M=2e5+5;
struct node{int a,b,c;int ans,cnt;
}a[N],A[N];
int n,m,len;
int ans[N];
namespace BIT{int tree[M];void add(int x,int k){for(;x<=m;x+=x&-x)tree[x]+=k;}int query(int x){int res=0;for(;x;x-=x&-x)res+=tree[x];return res;}
}
using BIT::add;
using BIT::query;
bool cmpa(node a,node b){if(a.a!=b.a)return a.a<b.a;if(a.b!=b.b)return a.b<b.b;return a.c<b.c;
}
bool cmpb(node a,node b){if(a.b!=b.b)return a.b<b.b;return a.c<b.c;
}
void cdq(int l,int r){if(l>=r)return;int mid=l+r>>1;cdq(l,mid);cdq(mid+1,r);sort(a+l,a+mid+1,cmpb);//按 b 排序sort(a+mid+1,a+r+1,cmpb);int i,j;for(i=mid+1,j=l;i<=r;i++){while(j<=mid&&a[j].b<=a[i].b){add(a[j].c,a[j].cnt);//树状数组维护 cj++;}a[i].ans+=query(a[i].c);}for(i=l;i<j;i++)add(a[i].c,-a[i].cnt);
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++)cin>>A[i].a>>A[i].b>>A[i].c;sort(A+1,A+1+n,cmpa);//按 a 排序,保证分开的区间前后 a 相对有序int tot=1;for(int i=2;i<=n;i++){//将点去重简化计算if(A[i].a!=A[i-1].a||A[i].b!=A[i-1].b||A[i].c!=A[i-1].c){a[++len]={A[i-1].a,A[i-1].b,A[i-1].c,0,tot};tot=1;}else{tot++;}}a[++len]={A[n].a,A[n].b,A[n].c,0,tot};cdq(1,len);for(int i=1;i<=len;i++)ans[a[i].ans+a[i].cnt-1]+=a[i].cnt;for(int i=0;i<n;i++)cout<<ans[i]<<'\n';return 0;
}

P3374 【模板】树状数组 1

单点加,区间查。

其实 CDQ 分治也能维护这个东西。

把对 \([l,r]\) 的查询转成对 \([1,l-1]\)\([1,r]\) 的查询,对于查询 \([1,x]\),由于只有位置 \(y \le x\) ,且时间早于这个查询的修改能对这个查询造成影响,所以这就是一个二维偏序问题!

二维偏序果断想到 CDQ。我们按时间顺序保存每个操作,对于区间 \([l,mid]\)\([mid+1,r]\),双指针 \(i,j\) 分别表示 \([l,mid]\)\([mid+1,r]\)。由于我们先按时间顺序保存了每个操作,所以 \([l,mid]\)\([mid+1,r]\) 之间的相对时间大小不变。归并操作区间,按操作位置排序,处理 \([l,mid]\) 中的操作时,如果是修改则累加贡献;处理 \([mid+1,r]\) 中的操作时,如果是查询则将贡献保存到答案数组中。然后输出答案数组就好了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
struct node{int op,pos,val,id;//op=1修改,=2查询[1,r],=3查询[1,l-1]//pos:操作的位置,val:修改的值/第val个答案//id:时间顺序bool operator <(const node &n)const{if(pos!=n.pos)return pos<n.pos;return id<n.id;}
}q[N*3],tmp[N*3];//tmp:归并数组
int n,m,cnt;
int p,ans[N];
void cdq(int l,int r){if(l==r)return;int mid=l+r>>1;cdq(l,mid);cdq(mid+1,r);int i,j,k,sum=0;for(i=l,j=mid+1,k=l;i<=mid&&j<=r;){//累加贡献if(q[i]<q[j]){if(q[i].op==1)sum+=q[i].val;tmp[k++]=q[i++];}else{//将贡献累加到答案数组if(q[j].op==2)ans[q[j].val]-=sum;if(q[j].op==3)ans[q[j].val]+=sum;tmp[k++]=q[j++];}}for(;i<=mid;){if(q[i].op==1)sum+=q[i].val;tmp[k++]=q[i++];}for(;j<=r;){if(q[j].op==2)ans[q[j].val]-=sum;if(q[j].op==3)ans[q[j].val]+=sum;tmp[k++]=q[j++];}for(i=l;i<=r;i++)q[i]=tmp[i];
}
int main(){cin>>n>>m;for(int i=1;i<=n;i++){cnt++;q[cnt].op=1;q[cnt].pos=i;cin>>q[cnt].val;}for(int i=1;i<=m;i++){int op;cin>>op;if(op==1){cnt++;q[cnt].op=1;q[cnt].id=i;cin>>q[cnt].pos>>q[cnt].val;}else{//拆分询问p++;cnt++;q[cnt].op=2;q[cnt].val=p;q[cnt].id=i;cin>>q[cnt].pos;q[cnt].pos--;cnt++;q[cnt].op=3;q[cnt].val=p;q[cnt].id=i;cin>>q[cnt].pos;}}cdq(1,cnt);for(int i=1;i<=p;i++)cout<<ans[i]<<'\n';return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/833902.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apple Logic Pro 11.1 - 专业音乐制作 (音频编辑)

Apple Logic Pro 11.1 - 专业音乐制作 (音频编辑)Apple Logic Pro 11.1 - 专业音乐制作 (音频编辑) Logic Pro 配备全新 AI 功能,引领音乐创作再上新阶 请访问原文链接:https://sysin.org/blog/apple-logic-pro/ 查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org…

【Civit3D 2025下载与安装教程】

1、安装包 「Civil3d_2025」: 链接:https://pan.quark.cn/s/b05281a72f24 提取码:PUWx 「Civil3D2020」: 链接:https://pan.quark.cn/s/61c01d7bd533 提取码:RbML 2、安装教程(建议关闭杀毒软件) 1) 双击Setup.exe安装,弹窗安装对话框2) 勾选‘我同意。。…

[Flask]SSTI 1

[Flask]SSTI 1 根据题目判断这是一道SSTI参数处理不当的漏洞,打开页面显示Hello guest,猜测参数名为user发现页面无任何变化,于是尝试name,成功回显尝试{{2*2}},页面显示4,存在SSTI注入构造payload,成功执行任意代码 # payload的解释:获取eval函数并执行任意代码?name=…

凯宇星辉:CRM助力实现数字化“领跑”转型

近日,第14届中国轻工业信息化大会及智能居家展洽会在重庆举行。会上同步发布了中国轻工业数字化转型“领跑者”相关案例成果287项,涉及家电、五金、照明、酿酒食品等行业,其中,纷享销客合作客户辽宁凯宇星辉实业集团有限公司(以下简称“凯宇星辉”)提报的《基于CRM系统的…

零售行业的高效法宝:项目管理软件的应用与选择

在零售行业,团队协作的效率直接影响到市场反应速度和客户满意度。商品的上下架、库存管理、促销活动的策划与执行、跨部门的沟通与协作……每一个环节都需要精准的协调。而在这些纷繁复杂的任务中,项目管理软件正成为零售行业的关键工具,帮助团队理清思路、优化流程、提升执…

关于从其他程序切回word文档时卡顿问题的解决办法

1.点击其他命令 2.点击加载项,选择对应的加载项,点击转到 3.如下图操作后,重启word即可 原文地址:https://blog.csdn.net/qq_45603855/article/details/115732189

ABB机器人DSQC639主板维修

ABB机器人的主板,作为这一高科技产物的中枢大脑,其出色的稳定性和可靠性无疑是确保机器人能够高效、持续运作的关键所在。一旦主板遭遇故障,整个机器人的运行将可能陷入瘫痪状态,严重影响生产效率与质量。以下,将深入探讨几种常见的ABB机器人主板故障及其相应的解决之道:…

前端开发中怎么把链接转为二维码并展示?

前端开发中我们应该怎么把一个通过代码生成二维码并展示呢?前言: 把一个链接生成一个二维码图片,这是我们前端非常常见的一个需求。那么我们应该如何做呢? 查看往期文章: 五分钟一百行代码,手写一个vue项目全局通用的toast提示组件 十五分钟两百行代码,手写一个vue项目全…

数字型注入(post)

数字型注入(post) 打开靶场,发现数字列表查询,点击查询发现POST请求和id、submit参数bp抓包,根据题目数字注入提示,尝试用1+1方式查询,+编码为%2B id=1%2B1&submit=%E6%9F%A5%E8%AF%A2成功回显 order by查询字段数,在为3的时候回显异常,判断字段数为2查库,查出数据…

设备管理系统功能拆解——设备报修维修管理

在生产过程中,设备的高效运作是确保生产顺利进行的重要基础。但是运行时间长了,设备难免会出现故障,如何高效地进行报修和维修管理,成为每个企业必须面对的问题。 通过有效的报修维修管理,企业可以快速响应设备故障,合理分配资源,从而最大限度地减少停机时间。这篇我们将…

设备管理系统功能拆解——设备维护保养管理

设备维护保养是企业日常运营中不可忽视的一环,无论是生产设备还是办公设备,都需要定期的维护和保养,以确保其正常运行。 设备维护保养的管理,不仅仅是日常工作,更是保障企业生产效率和设备寿命的关键,系统化管理维护保养工作可以显著提高设备的可靠性和使用寿命。 那么,…

Django框架表单基础

本节主要介绍一下Django框架表单(Form)的基础知识。Django框架提供了一系列的工具和库来帮助设计人员构建表单,通过表单来接收网站用户的输入,然后处理以及响应这些用户的输入。 6.1.1 HTML表单 Django框架表单是在HTML模板中设计完成的,其实类似于传统HTML Form表单的应用…