以前一直不会CDQ……然后经常听到dalao们说“这题直接CDQ啊”“CDQ不就秒了吗”的时候我只能瑟瑟发抖QAQ

CDQ分治

其实CDQ分治就是二分分治,每次将$[l,r]$的问题划分为$[l,mid]$和$[mid+1,r]$的子问题来解决,裸的时间复杂度是$O(nlogn)$。但是cdq的特殊要求是区间左半边的操作不会影响右半边的操作,一般适用于多次询问以及需要维护多个维度关键值的问题。(其实这种题也可以写树套树&KD树,dalao们又把我碾在了地上QAQ)

注意:cdq经常要在中间给数组排序,要使用归并来保证复杂度,直接sort的话或多一个$log$(Orzhjw)

三维偏序问题

三维偏序的著名不用我说了吧。。。几乎是所有人cdq的入门题,会cdq的人应该都写过吧。。。

先从一维偏序问题开始考虑,这就是一个经典的逆序对问题,用树状数组解决;

二维偏序的话可以对第一维排序,保证这一维有序后再对第二维建树状数组维护;

问题变成了三维,排序+树状数组也只能解决两维,还有一维就要用cdq分治(树套树)来搞:

注意到第一维排序后前一半的答案不会影响后一半的答案,于是把两边的区间分别按照第二维排序,然后建个树状数组解决;

(yrx:树套树好写易懂好调肯定写树套树啊)

代码:(BZOJ3262陌上花开 模板题)

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define lb(x) (x&(-x))
using namespace std;
struct task{
int a,b,c,tot,ans;
task(){
tot=;
}
}q[],s[];
int n,m,tot=,ans[],t[];
bool cmp(task a,task b){
if(a.a!=b.a)return a.a<b.a;
if(a.b!=b.b)return a.b<b.b;
if(a.c!=b.c)return a.c<b.c;
return false;
}
bool _cmp(task a,task b){
if(a.b!=b.b)return a.b<b.b;
if(a.c!=b.c)return a.c<b.c;
if(a.a!=b.a)return a.a<b.a;
return false;
}
void add(int x,int v){
for(;x<=m;x+=lb(x)){
t[x]+=v;
}
}
int sum(int x){
int ret=;
for(;x;x-=lb(x)){
ret+=t[x];
}
return ret;
}
void cdq(int l,int r){
if(l==r){
s[l].ans+=s[l].tot-;
return;
}
int mid=(l+r)/;
cdq(l,mid);
cdq(mid+,r);
sort(s+l,s+mid+,_cmp);
sort(s+mid+,s+r+,_cmp);
int j=l;
for(int i=mid+;i<=r;i++){
while(j<=mid&&s[j].b<=s[i].b)add(s[j].c,s[j].tot),j++;
s[i].ans+=sum(s[i].c);
}
for(int i=l;i<j;i++)add(s[i].c,-s[i].tot);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].c);
q[i].ans=;
}
sort(q+,q+n+,cmp);
for(int i=;i<=n;i++){
if(i!=&&q[i].a==q[i-].a&&q[i].b==q[i-].b&&q[i].c==q[i-].c)s[tot].tot++;
else s[++tot]=q[i],s[tot].tot=;
}
cdq(,tot);
sort(s+,s+tot+,cmp);
for(int i=;i<=tot;i++)ans[s[i].ans]+=s[i].tot;
for(int i=;i<=n;i++)printf("%d\n",ans[i]);
return ;
}

(这个代码多了一个log跑的巨慢无比)

一些刷题记录:

【BZOJ2176】天使玩偶

KD树大法好

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define inf 1000000000
using namespace std;
struct node{
int d[],mi[],mx[],ls,rs;
}t[];
int D,n,m,rt,x,y,op,tot=,ans;
bool cmp(node a,node b){
return a.d[D]!=b.d[D]?a.d[D]<b.d[D]:a.d[D^]<b.d[D^];
}
void pushup(int u){
int l=t[u].ls,r=t[u].rs;
if(l){
t[u].mi[]=min(t[u].mi[],t[l].mi[]);
t[u].mi[]=min(t[u].mi[],t[l].mi[]);
t[u].mx[]=max(t[u].mx[],t[l].mx[]);
t[u].mx[]=max(t[u].mx[],t[l].mx[]);
}
if(r){
t[u].mi[]=min(t[u].mi[],t[r].mi[]);
t[u].mi[]=min(t[u].mi[],t[r].mi[]);
t[u].mx[]=max(t[u].mx[],t[r].mx[]);
t[u].mx[]=max(t[u].mx[],t[r].mx[]);
}
}
void build(int &u,int l,int r,int d){
D=d;
int mid=(l+r)/;
u=mid;
nth_element(t+l,t+u+,t+r+,cmp);
t[u].mi[]=t[u].mx[]=t[u].d[];
t[u].mi[]=t[u].mx[]=t[u].d[];
if(u>l)build(t[u].ls,l,mid-,D^);
if(u<r)build(t[u].rs,mid+,r,D^);
pushup(u);
}
void ins(int k){
int d=,now=rt;
for(;;){
t[now].mi[]=min(t[now].mi[],t[k].mi[]);
t[now].mi[]=min(t[now].mi[],t[k].mi[]);
t[now].mx[]=max(t[now].mx[],t[k].mx[]);
t[now].mx[]=max(t[now].mx[],t[k].mx[]);
if(t[k].d[d]>=t[now].d[d]){
if(!t[now].rs){
t[now].rs=k;
return;
}
now=t[now].rs;
}else{
if(!t[now].ls){
t[now].ls=k;
return;
}
now=t[now].ls;
}
d^=;
}
}
int dis(int u,int x,int y){
int ret=;
if(x<t[u].mi[])ret+=t[u].mi[]-x;
if(x>t[u].mx[])ret+=x-t[u].mx[];
if(y<t[u].mi[])ret+=t[u].mi[]-y;
if(y>t[u].mx[])ret+=y-t[u].mx[];
return ret;
}
int query(int u,int x,int y){
int l,r,now;
now=abs(t[u].d[]-x)+abs(t[u].d[]-y);
ans=min(ans,now);
if(t[u].ls)l=dis(t[u].ls,x,y);
else l=inf;
if(t[u].rs)r=dis(t[u].rs,x,y);
else r=inf;
if(l<r){
if(l<ans)query(t[u].ls,x,y);
if(r<ans)query(t[u].rs,x,y);
}else{
if(r<ans)query(t[u].rs,x,y);
if(l<ans)query(t[u].ls,x,y);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%d%d",&t[i].d[],&t[i].d[]);
}
build(rt,,n,);
tot=n;
for(int i=;i<=m;i++){
scanf("%d%d%d",&op,&x,&y);
if(op==){
++tot;
t[tot].mi[]=t[tot].mx[]=t[tot].d[]=x;
t[tot].mi[]=t[tot].mx[]=t[tot].d[]=y;
ins(tot);
}else{
ans=inf;
query(rt,x,y);
printf("%d\n",ans);
}
}
return ;
}

【BZOJ1176】Mokia

下面一题的加强版,必须要写cdq

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define lb(x) (x&(-x))
using namespace std;
struct node{
int v,x,y,op,d,id;
}q[],tmp[];
int s,w,x,y,a,b,k,op,cnt=,tot=,aans[],ans[],t[];
bool cmp(node a,node b){
if(a.x!=b.x)return a.x<b.x;
if(a.y!=b.y)return a.y<b.y;
return a.op<b.op;
}
void add(int u,int x){
for(;u<=w;u+=lb(u)){
t[u]+=x;
}
}
int query(int u){
int ret=;
for(;u;u-=lb(u)){
ret+=t[u];
}
return ret;
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)/,L=l,R=mid+;
for(int i=l;i<=r;i++){
if(q[i].v<=mid&&!q[i].op)add(q[i].y,q[i].d);
if(q[i].v>mid&&q[i].op)ans[q[i].id]+=query(q[i].y)*q[i].d;
}
for(int i=l;i<=r;i++){
if(q[i].v<=mid&&!q[i].op)add(q[i].y,-q[i].d);
}
for(int i=l;i<=r;i++){
if(q[i].v<=mid)tmp[L++]=q[i];
else tmp[R++]=q[i];
}
for(int i=l;i<=r;i++)q[i]=tmp[i];
cdq(l,mid);
cdq(mid+,r);
}
int main(){
scanf("%d%d",&s,&w);
for(;;){
scanf("%d",&op);
if(op==){
scanf("%d%d%d",&x,&y,&k);
q[++cnt]=(node){cnt,x,y,,k,};
}else if(op==){
scanf("%d%d%d%d",&x,&y,&a,&b);
tot++;
aans[tot]=s*(a-x+)*(b-y+);
q[++cnt]=(node){cnt,a,b,,,tot};
q[++cnt]=(node){cnt,a,y-,,-,tot};
q[++cnt]=(node){cnt,x-,b,,-,tot};
q[++cnt]=(node){cnt,x-,y-,,,tot};
}else break;
}
sort(q+,q+cnt+,cmp);
cdq(,cnt);
for(int i=;i<=tot;i++)printf("%d\n",ans[i]+aans[i]);
return ;
}

【BZOJ2683】简单题

KD树大法好(成功跑到垫底QAQ)

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int D;
struct kdnode{
int ls,rs,num,v,d[],mi[],mx[];
int &operator [](int x){
return d[x];
}
friend bool operator <(kdnode a,kdnode b){
return a[D]<b[D];
}
}t[],rb[],s;
int n,op,x1,yy,x2,y2,v,rt=,tot=,R=,ans=;
bool inside(int x1,int yy,int x2,int y2,int x3,int y3,int x4,int y4){
return x1<=x3&&x2>=x4&&yy<=y3&&y2>=y4;
}
bool outside(int x1,int yy,int x2,int y2,int x3,int y3,int x4,int y4){
return x1>x4||x2<x3||yy>y4||y2<y3;
}
void pushup(int u){
int l=t[u].ls,r=t[u].rs;
for(int i=;i<=;i++){
t[u].mi[i]=t[u].mx[i]=t[u][i];
if(l)t[u].mi[i]=min(t[u].mi[i],t[l].mi[i]);
if(l)t[u].mx[i]=max(t[u].mx[i],t[l].mx[i]);
if(r)t[u].mi[i]=min(t[u].mi[i],t[r].mi[i]);
if(r)t[u].mx[i]=max(t[u].mx[i],t[r].mx[i]);
}
t[u].num=t[l].num+t[r].num+t[u].v;
}
int query(int u,int x1,int yy,int x2,int y2){
int ret=;
if(!u)return ;
if(inside(x1,yy,x2,y2,t[u].mi[],t[u].mi[],t[u].mx[],t[u].mx[]))return t[u].num;
if(outside(x1,yy,x2,y2,t[u].mi[],t[u].mi[],t[u].mx[],t[u].mx[]))return ;
if(inside(x1,yy,x2,y2,t[u][],t[u][],t[u][],t[u][]))ret+=t[u].v;
ret+=query(t[u].ls,x1,yy,x2,y2)+query(t[u].rs,x1,yy,x2,y2);
return ret;
}
void ins(int &u,bool d){
if(!u){
u=++tot;
t[u][]=t[u].mi[]=t[u].mx[]=s[];
t[u][]=t[u].mi[]=t[u].mx[]=s[];
}
if(s[]==t[u][]&&s[]==t[u][]){
t[u].v+=s.v;
t[u].num+=s.v;
return;
}
if(s[d]<t[u][d])ins(t[u].ls,d^);
else ins(t[u].rs,d^);
pushup(u);
}
int rebuild(int l,int r,bool d){
if(l>r)return ;
int mid=(l+r)/;
D=d;
nth_element(rb+l,rb+mid,rb+r+);
t[mid]=rb[mid];
t[mid].ls=rebuild(l,mid-,d^);
t[mid].rs=rebuild(mid+,r,d^);
pushup(mid);
return mid;
}
int main(){
scanf("%d",&n);
for(;;){
scanf("%d",&op);
if(op==){
scanf("%d%d%d",&x1,&yy,&v);
//x1^=ans;
//yy^=ans;
//v^=ans;
s[]=x1;
s[]=yy;
s.num=s.v=v;
ins(rt,);
//printf("aa %d\n",tot);
if(tot==R){
//printf("zjtywwakioi\n");
for(int j=;j<=tot;j++){
rb[j]=t[j];
}
rt=rebuild(,tot,);
R+=;
}
}else if(op==){
scanf("%d%d%d%d",&x1,&yy,&x2,&y2);
//x1^=ans;
//yy^=ans;
//x2^=ans;
//y2^=ans;
printf("%d\n",(ans=query(rt,x1,yy,x2,y2)));
}else break;
}
return ;
}

【BZOJ1492】【NOI2007】Cash

CDQ分治维护凸包+斜率优化DP

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define eps 1e-7
#define inf 1e15
using namespace std;
struct task{
double x,y,a,b,k,r;
int v,id;
}q[],tmp[];
double gtk(int a,int b){
if(!b)return -inf;
if(fabs(q[a].x-q[b].x)<eps)return inf;
return (q[a].y-q[b].y)/(q[a].x-q[b].x);
}
int n,top,s[];
double f[];
bool cmp(task a,task b){
return a.k>b.k;
}
void cdq(int l,int r){
if(l==r){
f[l]=max(f[l],f[l-]);
q[l].y=f[l]/(q[l].a*q[l].r+q[l].b);
q[l].x=q[l].r*q[l].y;
return;
}
int mid=(l+r)/,L=l,R=mid+,j=;
for(int i=l;i<=r;i++){
if(q[i].id<=mid)tmp[L++]=q[i];
else tmp[R++]=q[i];
}
for(int i=l;i<=r;i++)q[i]=tmp[i];
cdq(l,mid);
top=;
for(int i=l;i<=mid;i++){
while(top>&&gtk(s[top-],s[top])<gtk(s[top-],i)+eps)top--;
s[++top]=i;
}
s[++top]=;
for(int i=mid+;i<=r;i++){
while(j<top&&gtk(s[j],s[j+])+eps>q[i].k)j++;
f[q[i].id]=max(f[q[i].id],q[s[j]].x*q[i].a+q[s[j]].y*q[i].b);
}
cdq(mid+,r);
L=l,R=mid+;
for(int i=l;i<=r;i++){
if(((q[L].x<q[R].x||(fabs(q[L].x-q[R].x)<eps&&q[L].y<q[R].y))||R>r)&&L<=mid)tmp[i]=q[L++];
else tmp[i]=q[R++];
}
for(int i=l;i<=r;i++)q[i]=tmp[i];
}
int main(){
scanf("%d%lf",&n,&f[]);
for(int i=;i<=n;i++){
scanf("%lf%lf%lf",&q[i].a,&q[i].b,&q[i].r);
q[i].k=-q[i].a/q[i].b;
q[i].id=i;
}
sort(q+,q+n+,cmp);
cdq(,n);
printf("%.2lf",f[n]);
return ;
}

【HDU5126】stars

毒瘤题,四维CDQ or 三维KD树,先咕

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

  1. BZOJ1173 CDQ分治 笔记

    目录 二维数据结构->cdq 预备知识 T1: 二维树状数组 T2:cdq分治 bzoj1176 mokia:Debug心得 一类特殊的CDQ分治 附: bzoj mokia AC代码 二维数据 ...

  2. CDQ分治笔记+例题

    CDQ分治是一种离线分治算法,它基于时间顺序对操作序列进行分治. 看这样一个问题: 在一个三维坐标系中,有若干个点,每个点都有对应的坐标 \((X_i , Y_i , Z_i)\) ,我们要对于每个点 ...

  3. cdq分治 笔记

    算法讲解 这个算法用于解决三维偏序问题. 三维偏序:给定 \(n\) 个三元组: \((a_i,b_i,c_i)\),求同时满足满足 \(a_i\le a_j,b_i\le b_j,c_i\le c_ ...

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

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

  5. 学习笔记 | CDQ分治

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

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

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

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

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

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

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

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

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

随机推荐

  1. Ubuntu 16.04安装Caffe的记录及FCN官方代码的配置

    相关内容搜集自官方文档与网络,既无创新性,也不求甚解,我也不了解Caffe,仅仅搭上之后做个记录,方便以后重装 安装依赖项sudo apt-get install libprotobuf-dev li ...

  2. LeetCode(15)3Sum

    题目如下: Python代码: def threeSum(self, nums): res = [] nums.sort() for i in xrange(len(nums)-2): if i &g ...

  3. ZBrush中如何清除遮罩

    在之前的学习中我们知道在ZBrush®中如何创建遮罩,在创建遮罩时怎样进行反转来选择反选遮罩,本文将详细讲解ZBrush中如何清除遮罩,当我们利用遮罩达到预期效果时就需要将遮罩清除了:或者在做了遮罩的 ...

  4. 第七章 Python之模块与包

    模块介绍 一个模块就是包含了一组功能的python文件(例如module.py,模块名是module),它从文件级别组织程序,更方便管理,这时我们不仅仅可以把这些文件当作脚本执行,还可以把他们当作模块 ...

  5. 04 SqlServer

    数据库的注释 –(两个横线) 主键表 外键表 主键,组合主键 SqlServer 使用附加 权限 主文件mdf 日志文件ldf 数据类型 char varchar nchar nvarchar cha ...

  6. Jmeter中模拟多用户执行多场景操作

    1.其实一个用户组就是一个场景(Thread Group).可以在一个测试计划中进行多个场景的执行,在测试计划下加一个全局的User Defined Variables,在这个里面可以设置执行总数to ...

  7. IOS - PDF合并

    #pragma mark - Merge PDF - (void)mergePDF { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSD ...

  8. BZOJ 1016 最小生成树计数(矩阵树定理)

    我们把边从小到大排序,然后依次插入一种权值的边,然后把每一个联通块合并. 然后当一次插入的边不止一条时做矩阵树定理就行了.算出有多少种生成树就行了. 剩下的交给乘法原理. 实现一不小心就会让程序变得很 ...

  9. Docker学习总结(13)——从零开始搭建Jenkins+Docker自动化集成环境

    本文只简单标记下大概的步骤,具体搭建各个部分的细节,还请自行搜索.第一.二部分只是对Jenkins和Docker的简单介绍,熟悉的同学请直接跳到第三部分. 一.关于Jenkins Jenkins简介 ...

  10. Java 学习(10):java 异常处理

    java 异常处理 异常发生的原因有很多,通常包含以下几大类: 用户输入了非法数据. 要打开的文件不存在. 网络通信时连接中断,或者JVM内存溢出. 三种类型的异常: 检查性异常: 最具代表的检查性异 ...