cdq分治

ps:先膜拜陈丹琦大神,Orz%%%。

作用

很多动态的题目都需要高级数据结构,代码量很大,这时候cdq分治就展现了它的强大。只要不强制在线,cdq分治就可以将动态转化为静态处理,而且代码复杂度低,易调试,是很不错的方法!

实现

cdq分治是将整个操作序列进行分治,分治步骤如下:
1.将序列拆分为两半,[L,mid]和[mid+1,R]。
2.考虑[L,mid]所有操作对[mid+1,R]的影响,并计算。
3.由于[L,mid]和[mid+1,R]是和[L,R]相同的子问题,于是递归处理。

在cdq分治之前,通常要让操作序列满足一定的顺序(比如按照时间排序,使[L,mid]所有操作在[mid+1,R]之前)。而在cdq分治时,也可能需要让操作序列再次满足一定的顺序。

cdq分治典型的应用是三维偏序,然而我并不知道三维偏序准确定义是什么,考虑这样一个题目:给出n个三维点(x,y,z),(xi,yi,zi)>=(xj,yj,zj)定义为xi>=xj且yi>=yj且zi>=zj。求每个点>=多少个其他点。

如果点是二维的,那么就可以先按照x第一关键字,y第二关键字排序,然后利用树状数组就可以计算每个点>=多少个其他点。然而点是三维的,不过我们可以用同样的想法。先按照x第一关键字,y第二关键字,z第三关键字排序。x不用管,y和z用数据结构来维护,但这样代码量就比较大了。

如果我们把每个点看做操作,排序过后一个点会影响前面的点吗?不会,只会影响后面的点,所以我们想到了cdq分治。将操作序列分为[L,mid]和[mid+1,R],然后按照y第一关键字,区间(左小右大)第二关键字排序,这样就可以不用管y了,而z和二维问题一样,用树状数组即可。最后再处理一下完全相同的节点就可以得到答案。

模板

以HDU5618为例。
这就是上面说的问题,不多解释了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100000,maxm=100000;int te,n,MAX,ans[maxn+5];
struct Point3D
{int x,y,z,id;bool operator == (const Point3D &a) {return x==a.x&&y==a.y&&z==a.z;}
};
Point3D p[maxn+5],tem[maxn+5];
struct FenwickTree
{int s[maxm+5];int lowbit(int x) {return x&(-x);}void Insert(int x,int tem) {while (x<=MAX) s[x]+=tem,x+=lowbit(x);}int Ask(int L,int R){int x,sum=0;L--;x=L;while (x) sum-=s[x],x-=lowbit(x);x=R;while (x) sum+=s[x],x-=lowbit(x);return sum;}
};
FenwickTree tr;bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{int tot=0,f=1;char ch=getchar(),lst='+';while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}if (lst=='-') f=-f;while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();x=tot*f;return Eoln(ch);
}
bool mycmp1(const Point3D &a,const Point3D &b)
{if (a.y<b.y) return true;if (a.y==b.y&&a.id<b.id) return true;return false;
}
void cdqBinary(int L,int R)
{if (L==R) return;int mid=L+(R-L>>1);for (int i=L;i<=mid;i++) tem[i]=p[i],tem[i].id=0;for (int i=mid+1;i<=R;i++) tem[i]=p[i];sort(tem+L,tem+1+R,mycmp1);for (int i=L;i<=R;i++)if (!tem[i].id) tr.Insert(tem[i].z,1); elseans[tem[i].id]+=tr.Ask(1,tem[i].z);for (int i=L;i<=R;i++) if (!tem[i].id) tr.Insert(tem[i].z,-1);cdqBinary(L,mid);cdqBinary(mid+1,R);
}
bool mycmp2(const Point3D &a,const Point3D &b)
{if (a.x<b.x) return true;if (a.x==b.x&&a.y<b.y) return true;if (a.x==b.x&&a.y==b.y&&a.z<b.z) return true;if (a.x==b.x&&a.y==b.y&&a.z==b.z&&a.id<b.id) return true;return false;
}
int main()
{freopen("cdqBinary.in","r",stdin);freopen("cdqBinary.out","w",stdout);readi(te);while (te--){readi(n);MAX=0;for (int i=1;i<=n;i++){readi(p[i].x);readi(p[i].y);readi(p[i].z);p[i].id=i;MAX=max(MAX,p[i].z);}sort(p+1,p+1+n,mycmp2);memset(ans,0,sizeof(ans));for (int i=n-1,tot=0;i>=1;i--){if (p[i]==p[i+1]) tot++; else tot=0;ans[p[i].id]=tot;//cdq分治处理时只往前统计相同三维点,所以要再向后统计一下相同三维点}cdqBinary(1,n);for (int i=1;i<=n;i++) printf("%d\n",ans[i]);}return 0;
}

整体二分

ps:先膜拜发明整体二分的我不知道是谁的人,Orz%%%

作用

和cdq分治一样,也是将动态转化为静态。

实现

整体二分也是对操作序列进行处理。但和cdq分治稍微有些不同,因为整体二分需要二分处理答案范围[L,R]:
1.如果L==R,说明答案已经确定了,将目前序列中所有询问的答案定为L(R)。
2.二分答案mid。
3.考虑序列中靠前的操作对靠后的询问的影响。
4.对序列进行分块,序列中<=mid的操作放到左边,>mid的放到右边。
5.递归处理相同子问题[L,mid]和[mid+1,R]。

整体二分经典应用是区间第K大。二分答案mid之后,按顺序遍历序列,如果是插入删除修改等操作,且数值<=mid,说明这些操作和[mid+1,R]无关,所以放到左边,如果数值>mid,说明这些操作和[L,mid]无关,所以放到右边,同时累加>mid的个数(比如使用树状数组)。如果是查询,先询问查询区间中>mid的个数,记为now,如果查询数值<now,说明该查询的答案<=mid,和[mid+1,R]无关,所以放到左边,但是查询数值要减去now(因为放到左边后[mid+1,R]的就没了),如果查询数值>=now,说明该查询的答案>mid,和[L,mid]无关,所以放到右边。

模板

以BZOJ3110为例。
这就是上面说的问题,不多解释了。下面的代码用到了树状数组区间增减,比线段树方便很多。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=50000,maxm=50000,MAXINT=((1<<30)-1)*2+1;int n,m,ans[maxm+5];
struct Work
{int id,td,L,R,k;
};
Work wk[maxm+5],tem[2][maxm+5];
struct FenwickTree
{LL s[2][maxn+5];void clear() {memset(s,0,sizeof(s));}int lowbit(int x) {return x&(-x);}void Insert(int L,int R,LL tem){int x;R++;x=L;while (x<=n) s[0][x]+=tem,s[1][x]+=tem*(L-1),x+=lowbit(x);x=R;while (x<=n) s[0][x]-=tem,s[1][x]-=tem*(R-1),x+=lowbit(x);}LL Ask(int L,int R){int x;LL sum=0;L--;x=L;while (x) sum-=s[0][x]*L-s[1][x],x-=lowbit(x);x=R;while (x) sum+=s[0][x]*R-s[1][x],x-=lowbit(x);return sum;}
};
FenwickTree tr;void AllBinary(int L,int R,int l,int r)
{if (l>r) return;if (L==R){for (int i=l;i<=r;i++) if (wk[i].td==2) ans[wk[i].id]=L;return;}int num[2],mid=L+(R-L>>1);num[0]=num[1]=0;for (int i=l;i<=r;i++)if (wk[i].td==1){if (wk[i].k<=mid) tem[0][++num[0]]=wk[i]; elsetem[1][++num[1]]=wk[i],tr.Insert(wk[i].L,wk[i].R,1);} else{LL now=tr.Ask(wk[i].L,wk[i].R);if (now<wk[i].k) wk[i].k-=now,tem[0][++num[0]]=wk[i]; elsetem[1][++num[1]]=wk[i];}for (int i=l;i<=r;i++) if (wk[i].td==1&&wk[i].k>mid) tr.Insert(wk[i].L,wk[i].R,-1);for (int i=1;i<=num[0];i++) wk[l+i-1]=tem[0][i];for (int i=1;i<=num[1];i++) wk[l+num[0]+i-1]=tem[1][i];AllBinary(L,mid,l,l+num[0]-1);AllBinary(mid+1,R,l+num[0],r);
}
int main()
{freopen("AllBinary.in","r",stdin);freopen("AllBinary.out","w",stdout);scanf("%d%d",&n,&m);int L=MAXINT,R=-MAXINT;for (int i=1;i<=m;i++){scanf("%d%d%d%d",&wk[i].td,&wk[i].L,&wk[i].R,&wk[i].k);wk[i].id=i;if (wk[i].td==1) L=min(L,wk[i].k),R=max(R,wk[i].k);}memset(ans,63,sizeof(ans));AllBinary(L,R,1,m);for (int i=1;i<=m;i++) if (ans[i]!=ans[0]) printf("%d\n",ans[i]);return 0;
}

效率

假设cdq分治和整体二分中的操作复杂度为T,则cdq分治和整体二分的效率均为 O(T∗log2(n)) O(T*log_2(n))。非常不错。

然而我这个蒟蒻要熟练掌握cdq分治和整体二分看来还需要很久……

cdq分治和整体二分相关推荐

  1. CDQ 分治与整体二分

    CDQ 分治与整体二分 CDQ 分治 主要是一种分治思想,常用于解决偏序问题. 例如三维偏序问题,我们采用的方法是先处理以第一关键字为区分的左区间.右区间内的答案,再处理左右区间互不干涉的答案. 四维 ...

  2. [CDQ分治与整体二分]个人对CDQ分治与整体二分的理解

    在线/离线:首要考虑 在线算法: 可以以序列化的方式一个一个的处理输入,不必事先知道所有输入数据 离线算法: 必须事先知道所有的输入数据 (例如选择排序就是一个离线算法,而插入排序则不是) 众所周知, ...

  3. 【cdq分治】cdq分治与整体二分学习笔记Part1.整体二分

    之所以把cdq分治和整体二分放在一起学习,是因为他们两个实在太像了-不管是做法还是代码- 感觉整体二分可能会比cdq分治稍微简单那么一点点?所以先学整体二分. 整体二分是对答案进行二分,其具体操作如下 ...

  4. 【cdq分治】cdq分治与整体二分学习笔记Part2.cdq分治

    上午的学习学会了整体二分,下午学了cdq分治 发现了二者的区别: 整体二分的主体是在不断地二分答案(把所有询问二分),而cdq分治则是在不断地二分操作. 当然同样的,cdq分治的复杂度也是与区间长度正 ...

  5. bzoj 4237: 稻草人(CDQ分治+单调栈+二分)

    4237: 稻草人 Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 1352  Solved: 594 [Submit][Status][Discus ...

  6. Loj#2880-「JOISC 2014 Day3」稻草人【CDQ分治,单调栈,二分】

    正题 题目链接:https://loj.ac/problem/2880 题目大意 给出平面上的nnn个点,然后求有多少个矩形满足 左下角和右上角各有一个点 矩形之间没有其他点 1≤n≤2×105,1≤ ...

  7. [总结]CDQ分治整体二分

    从昨天到现在除了90%的颓废时间一直在研究一些分治的姿势,主要就是CDQ分治和整体二分. 首先推荐一些学习资料: 陈丹琦 <从 < Cash > 谈一类分治算法的应用> 许昊然 ...

  8. 点分治+CDQ分治+整体二分全纪录

    点分治 点分治讲解 解决树上路径问题 经典例题:点分治(长度小于m的路径计数) 经典例题:点分治(聪聪可可) 经典例题:点分治(多个定值路径计数) 经典例题:点分治(采药) 经典例题:点分治+ST表+ ...

  9. 【BZOJ 3636】教义问答手册 (分治+整体二分+dp)

    description "汉中沃野如关中,四五百里烟蒙蒙.黄云连天夏麦熟,水稻漠漠吹秋风."--摘自 黄裳<汉中行> "泉岭精神不朽,汉中诸球永生." ...

最新文章

  1. RDKit:化合物骨架分析(基于Python3)
  2. android studio 导入、移除module、以及加载module中的jar包
  3. 一种比较兼容的Excel报表导出方法
  4. filter 中用spring StopWatch 监控请求执行时间
  5. 探索式测试的思维模型
  6. 研发协同平台持续集成Jenkins作业设计演进
  7. C++静态数据成员与静态函数(案例分析)
  8. Python基础----集合
  9. c语言常考的编程题,C语言操作题常考编程题库
  10. FLAT:又快又好的中文命名实体识别模型
  11. Windows中MySQL主从数据库搭建(三)
  12. gis等时圈怎么做_ArcGIS中输入风格一致的带圈数字
  13. EXCEL数组公式(3)---数组公式的基础应用,理解数组公式
  14. python电影名称词云_Python爬取最近上映的电影评论并生成词云——误杀
  15. SQL(08)_SQL约束
  16. 无损音乐知识收集1(转)
  17. 156 13.67用matlab答案,matlab数学实验答案_(胡良剑)版本
  18. Resnet网络中数字的含义以及连接方式
  19. Vue - 调用接口导出 excel 表格至本地
  20. 解决MacBook浏览器打开北京工作居住证系统问题

热门文章

  1. 高通平台开发系列讲解(外设篇)DSI分屏
  2. to_date的用法
  3. Oracle中TO_DATE用法
  4. 【连接池】Tomcat 连接池中 maxActive,maxWait,maxAge,testOnBorrow,testWhileIdle等选项的作用
  5. Gensim官方教程翻译(二)——语料库与向量空间(Corpora and Vector Spaces)
  6. bigquery_为什么BigQuery Omni很重要
  7. 软件测试需要具备的技能(软技能硬技能)
  8. 一、pyside6 安装
  9. MySQL--SQL执行顺序详解
  10. Meta Connect汇总:Quest Pro发布,主打生产力场景