CDQ的嵌套

上一篇博客介绍了一下CDQ的入门思想。这里再介绍一下它的进阶,CDQ套CDQ。其实如果对入门思想掌握的透彻,嵌套也是很容易掌握的,思想是一样的。

什么是嵌套

简单地说,有的问题,如果用一重CDQ来分治一个维度后,在合并时,还无法仅借助一层数据结构(如树状数组)来计算左区间对右区间元素的影响。那这时,我们可以选择再用一重CDQ来分治下一维度,达到再降维的效果。

以一道四维偏序的变形问题为例

HDU上的一道题,stars

题意

三维空间下,有两种操作,1.加入一个点;2.查询当前指定长方体空间内含多少个点。

思路

这题可以类比二维下矩形求和拆成四个矩形前缀求和。长方体求和可以拆成八个长方体前缀求和,根据容斥做一下加减。

由于加入操作和查询操作可能交替进行,就必须考虑操作时间的影响。解决了上一篇博客的若干问题后,不难想到,这一题,我们必须考虑四个维度:时间和三个坐标x,y,z。因此,按时间排序(即读入顺序),对x分治,在根据x进行合并时,我们发现无法简单统计贡献,因为还剩下两维(y,z),也就是说我们需要二维树状数组才能统计,但这样空间开销难以接受。因此,这一重CDQ我们不统计贡献,只做按x排序这件事,但这样时间顺序会乱掉,所以还要标记每个元素的操作时间原本属于左区间还是右区间。拷贝一份处理完的新序列,这样一来,我们再对y进行第二重CDQ分治,在根据y来合并时,此时x的影响已经处理掉了,因此需要再判断的就是时间和操作类型,只有时间属于左区间的改操作元素才可能对时间属于右区间的查操作元素有贡献。这时,剩下的一维z可以离散化后用树状数组维护。

代码

#include<bits/stdc++.h>
#define dd(x) cout<<#x<<" = "<<x<<" "
#define de(x) cout<<#x<<" = "<<x<<"\n"
#define sz(x) int(x.size())
#define All(x) x.begin(),x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
typedef priority_queue<int> BQ;
typedef priority_queue<int,vector<int>,greater<int> > SQ;
const int maxn=5e4+10,mod=1e9+7,INF=0x3f3f3f3f;
int fwk[maxn<<1];
void upd(int p,int c)
{
for (int i=p;i<(maxn<<1);i+=i&-i)
fwk[i]+=c;
}
int qry(int p)
{
int res=0;
for (int i=p;i;i-=i&-i)
res+=fwk[i];
return res;
}
struct node
{
int ti,x,y,z,ty,id;
};
node p[maxn<<3],tmp[maxn<<3],tmp2[maxn<<3];
int ans[maxn],id,v[maxn<<1],tot,cnt;
inline void read(int x,int y,int z,int id=0,int ty=0)
{
p[++cnt].x=x,p[cnt].y=y,p[cnt].z=z,p[cnt].id=id,p[cnt].ty=ty;
}
inline int hs(int x)
{
return lower_bound(v+1,v+1+tot,x)-v;
}
void cdq2(int l,int r)
{
if (l>=r)
return;
int m=(l+r)>>1;
cdq2(l,m);
cdq2(m+1,r);
int i=l,j=m+1,k=0;
while (i<=m&&j<=r)
{
if (tmp2[i].y==tmp2[j].y? tmp2[i].ty==0 : tmp2[i].y<tmp2[j].y)
{
if (tmp2[i].ti==0&&tmp2[i].ty==0)
upd(hs(tmp2[i].z),1);
tmp[k++]=tmp2[i++];
}
else
{
if (tmp2[j].ti&&tmp2[j].ty)
ans[tmp2[j].id]+=tmp2[j].ty*qry(hs(tmp2[j].z));
tmp[k++]=tmp2[j++];
}
}
while (j<=r)
{
if (tmp2[j].ti&&tmp2[j].ty)
ans[tmp2[j].id]+=tmp2[j].ty*qry(hs(tmp2[j].z));
tmp[k++]=tmp2[j++];
}
for (int t=l;t<i;++t)
if (tmp2[t].ti==0&&tmp2[t].ty==0)
upd(hs(tmp2[t].z),-1);
while (i<=m)
tmp[k++]=tmp2[i++];
for (int i=0;i<k;++i)
tmp2[l+i]=tmp[i];
}
void cdq1(int l,int r)
{
if (l>=r)
return;
int m=(l+r)>>1;
cdq1(l,m);
cdq1(m+1,r);
int i=l,j=m+1,k=0;
while (i<=m&&j<=r)
{
if (p[i].x==p[j].x? (p[i].ty==0) : (p[i].x<p[j].x))
p[i].ti=0, tmp[k++]=p[i++];
else
p[j].ti=1, tmp[k++]=p[j++];
}
while (i<=m)
p[i].ti=0, tmp[k++]=p[i++];
while (j<=r)
p[j].ti=1, tmp[k++]=p[j++];
for (i=0;i<k;++i)
p[l+i]=tmp[i];
for (i=0;i<k;++i)
tmp2[i]=tmp[i];
cdq2(0,k-1);
}
int main()
{
int T;
cin>>T;
while (T--)
{
memset(ans,0,sizeof(ans));
tot=0,cnt=0,id=0;
int n;
scanf("%d",&n);
for (int i=1;i<=n;++i)
{
int op,x1,x2,y1,y2,z1,z2;
scanf("%d",&op);
if (op==1)
{
scanf("%d%d%d",&x1,&y1,&z1);
read(x1,y1,z1);
}
else
{
scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2);
x1--,y1--,z1--;
read(x2,y2,z2,++id,1);
read(x1,y1,z2,id,1);
read(x1,y2,z2,id,-1);
read(x2,y1,z2,id,-1);
read(x2,y2,z1,id,-1);
read(x1,y1,z1,id,-1);
read(x1,y2,z1,id,1);
read(x2,y1,z1,id,1);
v[++tot]=z2;
}
v[++tot]=z1;
}
sort(v+1,v+1+tot);
cdq1(1,cnt);
for (int i=1;i<=id;++i)
printf("%d\n",ans[i]);
}
return 0;
}

总结

简单总结一下,可以cdq分治的这类题,就是用cdq来逐步降维,把高维问题简化为我们熟悉的低维问题。当然数据结构也可以起到降维的效果,但缺点一般是空间开销较大。因此在不强制在线的情况下,CDQ分治不妨作为一个降维工具

CDQ分治的嵌套的更多相关文章

  1. $CDQ$分治总结

    A.\(CDQ\) 分治 特别基础的教程略. \(CDQ\)分治的优缺点: ( 1 )优点:代码量少,常数极小,可以降低处理维数. ( 2 )缺点:必须离线处理. \(CDQ\)分治与其他分治最本质的 ...

  2. CDQ分治嵌套模板:多维偏序问题

    CDQ分治2 CDQ套CDQ:四维偏序问题 题目来源:COGS 2479 偏序 #define LEFT 0 #define RIGHT 1 struct Node{int a,b,c,d,bg;}; ...

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

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

  4. hdu5618 (三维偏序,cdq分治)

    给定空间中的n个点,问每个点有多少个点小于等于自己. 先来分析简单的二维的情况,那么只要将x坐标排序,那么这样的问题就可以划分为两个子问题,,这样的分治有一个特点,即前一个子问题的解决是独立的,而后一 ...

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

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

  6. 【BZOJ2253】[2010 Beijing wc]纸箱堆叠 cdq分治

    [BZOJ2253][2010 Beijing wc]纸箱堆叠 Description P 工厂是一个生产纸箱的工厂.纸箱生产线在人工输入三个参数 n p a , , 之后,即可自动化生产三边边长为 ...

  7. 【BZOJ2253】纸箱堆叠 [CDQ分治]

    纸箱堆叠 Time Limit: 30 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description P 工厂是一个生产纸箱的工厂. 纸 ...

  8. Codeforces 669E cdq分治

    题意:你需要维护一个multiset,支持以下操作: 1:在某个时间点向multiset插入一个数. 2:在某个时间点在multiset中删除一个数. 3:在某个时间点查询multiset的某个数的个 ...

  9. BZOJ 2683 简单题 ——CDQ分治

    [题目分析] 感觉CDQ分治和整体二分有着很本质的区别. 为什么还有许多人把他们放在一起,也许是因为代码很像吧. CDQ分治最重要的是加入了时间对答案的影响,x,y,t三个条件. 排序解决了x ,分治 ...

随机推荐

  1. 数据库分库分表策略之MS-SQL读写分离方案

    MS-SQL读写分离将从以下知识点进行展开: 以下截图内容来自博主:https://www.cnblogs.com/echosong/p/3603270.html 1.本地发布(写库如:centerd ...

  2. 解决go mod或go get时`x509: certificate signed by unknown authority`错误

    一般go get私有仓库时会出现如下错误: go: xxx@v0.0.0-20190918102752-bb51b27911ca: unrecognized import path "xxx ...

  3. 【web安全】浅谈web安全之XSS

    XSS定义 XSS, 即为(Cross Site Scripting), 中文名为跨站脚本, 是发生在目标用户的浏览器层面上的,当渲染DOM树的过程成发生了不在预期内执行的JS代码时,就发生了XSS攻 ...

  4. .NET webapi 的单元测试

    public abstract class MirAPIUnitTestCommon { public abstract string GetBaseAddress(); /// <summar ...

  5. SQL学习——SELECT INTO和INSERT INTO SELECT

    原文链接 SELECT INTO 作用 SELECT INTO 语句从一个表中复制数据,然后将数据插入到另一个新表中. SELECT INTO 语法 我们可以把所有的列都复制到新表中: SELECT ...

  6. MVP架构的一个小例子

    主角: MVP是一种编程的架构模式,M=Model,负责提供数据:V=View,负责显示数据:P=Presenter,负责处理数据. 应用例子: csharp写的一个qq机器人. 一.Model层 获 ...

  7. paypal支付 NVP支付 paypal 手续费 GetTransactionDetails

    主要内容: 本文章主要讲解的是NVP的对接,以最简单的接口案例,讲解一下对接NVP的方案. 先提供下paypal 官方文档的主要功能对接说明,如下 1.请求API 服务器端点 描述 https://a ...

  8. 【坑】Spring中抽象父类属性注入,子类调用父类方法使用父类注入属性

    运行环境 idea 2017.1.1 spring 3.2.9.RELEASE 需求背景 需要实现一个功能,该功能有2个场景A.B,大同小异 抽象一个抽象基类Base,实现了基本相同的方法BaseMe ...

  9. 第一章、前端之html

    目录 第一章.前端之html 一. html介绍 第一章.前端之html 一. html介绍 web服务本质 import socket sk = socket.socket() sk.bind((& ...

  10. Spring Boot Start 打包方式装B指南

    项目结构如下: test包:实际的代码 spring-boot-start-test包:start 配置包 代码详细配置如下 https://github.com/fqybzhangji/spring ...