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. JS中json数组多字段排序方法(解决兼容性问题)(转)

    前端对一个json数组进行排序,用户需要动态的根据自己的选择来对json数据进行排序. 由于后台表设计问题所以不能用sql进行排序,这里用到了js的sort方法. 如果对单字段排序,那么很简单,一个s ...

  2. Iedis - Redis 在IDEA中的可视化工具破解

    2.如何破解 // 如果你没有改动IDEA的话,IDEA的插件库在这个目录下C:\Users\Administrator\.IntelliJIdea2017.3\config\plugins\Iedi ...

  3. luogu P3750 [六省联考2017]分手是祝愿

    luogu loj 可以发现在最优策略中,每种操作最多只会做一次,并且操作的先后顺序并不会影响答案,所以考虑从后往前扫,碰到一个\(1\)就对这个位置\(i\)进行操作,这样的操作一定是最优策略.记最 ...

  4. NLog Helpper日志帮助类配置和使用

    1.帮助类  (首先需要引入NLog.dll) using System; namespace XXXXXX { /// <summary> /// 用法实例 : NLogTest.Nlo ...

  5. c++容易混淆知识点

    C ++令人困惑的知识点1 函数传递指针和传递引用之间的区别? 1 GT;指针定义可能未初始化,但引用不可能; 2 - ;引用只能与一个实体组合,指针可以与多个实体组合; 3 GT;加法和减法的含义是 ...

  6. Spring Cloud(六)服务网关 zuul 快速入门

    服务网关是微服务架构中一个不可或缺的部分.通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由.均衡负载功能之外,它还具备了权限控制等功能.Spring Cloud Netflix中 ...

  7. 【Distributed】分布式Session一致性问题

    一.概述 1.1 什么是Session 1.2 Session实现原理 1.3 Session常见问题 Session 保证在那里? 关闭浏览器Session会失效吗 服务器集群之后,Session产 ...

  8. VLC 可能的 XML parser error 解决

    由于 VLC 设置不当 (通常是动了 skin 选项……),再次加载时 VLC 不能正常启动,并报如下错误: [00007f7dd003b670] xml xml reader error: XML ...

  9. 图像处理---《在图片上打印文字 windows+GDI+TrueType字体》

    图像处理---<在图片上打印文字  windows+GDI+TrueType字体> 刚开始使用的是putText()函数做,缺陷是只能显示非中文: 接着,看大多数推荐Freetype库来做 ...

  10. layui多图上传实现删除功能

    在使用layui的多图上传时发现没有删除功能 在网上搜索解决办法时有的感觉太复杂有的不符合自己所需要的所以就自己动手 下面附上代码 HTML: <div class="layui-up ...