cdq分治和整体二分
来源:互联网 发布:java 进阶书籍 编辑:程序博客网 时间:2024/04/30 10:47
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); else ans[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]; else tem[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]; else tem[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分治和整体二分的效率均为
然而我这个蒟蒻要熟练掌握cdq分治和整体二分看来还需要很久……
- cdq分治和整体二分
- CDQ分治&&整体二分
- [总结]CDQ分治&整体二分
- 整体二分<QAQ> && CDQ分治
- 主席树+CDQ分治+整体二分
- 整体二分及cdq分治题库
- CDQ分治与整体二分小结
- cdq分治&整体二分学习缓存
- 【cdq分治】cdq分治与整体二分学习笔记Part2.cdq分治
- 【cdq分治】cdq分治与整体二分学习笔记Part1.整体二分
- [CDQ分治与整体二分]个人对CDQ分治与整体二分的理解
- [CDQ分治与整体二分]个人对CDQ分治与整体二分的理解
- 整体二分&cdq分治 ZOJ 2112 Dynamic Rankings
- HDU 5412 CRB and Queries(整体二分 | CDQ分治)
- [BZOJ3110][ZJOI2013]K大数查询-CDQ分治-整体二分
- 2017 暑假艾教集训 day9(整体二分 + cdq分治 cdq真是我女神!!!)
- HDU 5412 CRB and Queries 求区间第k小 CDQ分治+整体二分
- 整体二分&CDQ分治:[BZOJ2527][POI2011] meteors [BZOJ3295][CQOI2011] 动态逆序对
- Spring 在使用MVC时的两个context
- Nginx服务器安装及配置文件详解
- 线性表和顺序表区别
- 学习笔记:数组四大排序方法
- Linux常用命令总结
- cdq分治和整体二分
- ajax 流程
- Hihocoder #1043 : 完全背包
- bzoj1305: [CQOI2009]dance跳舞
- 关于导入Extjs项目Eclipse内存溢出
- Hi3516A项目-烧写文件系统到开发板Flash
- 用于数组的new和delete
- Nginx配置location总结及rewrite规则写法
- 41. First Missing Positive Hard