CDQ分治正确的入门姿势
来源:互联网 发布:js删除指定class的div 编辑:程序博客网 时间:2024/04/30 09:58
CDQ分治(陈丹琦分治)的思想就是降维。
先来看个题:
1.在数轴上(1~1e9),初始化每点为0。两种操作:1 a b(将点a加b),2 a b(询问区间a—b值的总和)。
很快会想到线段树或树状数组,因为区间过大,所以离线化一下就好了,这里因为前缀和需要,说以我们就使用树状数组。先把操作2才分开记录下询问的id编号,这个询问的答案就是两个前缀和相减,这里我们来深层的挖一下这么做的原因:其实这里有两个维度:位置x和时间t,时间t是按输入顺序自动排序好的,所以我们容易忽视时间t维度,其实时间维度与位置维度是完全一样的,我们不妨按位置x排序(同样把操作2分开,操作1和操作2丢在一起排),按时间t来树状数组,也会得到正确答案。这是为什么呢?因为不管我们是按位置做排序时间做树状数组,还是按时间排序按位置做树状数组,都是得到的两个维度前缀和的相交部分(可能解释得不太到位0.0,请见谅)。
总结上面这题,就是:时间t维度和位置x维度,一个维度排序,另一个维度用树状数组。
此时进入正题,再来看一题:
2.在平面上,(x,y:1~1e9),初始化每点为0。两种操作:1 a b c(将点a,b加c),2 a1 b1 a2 b2(询问矩形区域a1,b1—a2,b2值的总和)。
很快就会想到二维线段树或二维树状数组再加个离散化,但是如果询问次数大于10000,离散化后可能还要开20000*20000的空间,所以最多只能开一维的树状数组,参考上一题的降维解法,这一题有3个维度:时间t 位置x 位置y,我们二分t维度,排序x维度,树状数组y维度,就可以成功降维了。
总结上面这题:通过二分,排序,树状数组降维。
分析上面这题:每个维度是可以前缀思想来处理的 才可以用这个方法!因为只有这样,前面的修改操作才会有且仅有一次地影响到后面的查询操作(可能解释得不太到位0.0,请见谅)。
下面再来看一题:
3.在空间上,(x,y,z:1~1e9),初始化每点为0。两种操作:1 a b c d(将点a,b,c加d),2 a1 b1 c1 a2 b2 c2(询问长方体区域a1,b1,c1—a2,b2,c2值的总和)。
参考上面一题降维解法,这一题有4个维度:时间t 位置x 位置y位置z,我们二分t维度,二分x维度,排序y维度,树状数组z维度,就可以成功降维了。
下面是与第3题类似的一题:
http://acm.hdu.edu.cn/showproblem.php?pid=5126
分析看上面,代码看下面0.0,个人觉得这代码足够清晰,不用加多余注释,哈哈。
#include<cstring>#include<string>#include<iostream>#include<queue>#include<cstdio>#include<algorithm>#include<map>#include<cstdlib>#include<cmath>#include<vector>//#pragma comment(linker, "/STACK:1024000000,1024000000");using namespace std;#define INF 0x3f3f3f3fstruct node{ int x,y,z; int kind,id; node(){} node(int _x,int _y,int _z,int _k,int _id):x(_x),y(_y),z(_z),kind(_k),id(_id){}};vector<node>q,q1,q2;vector<int>mdzz;int ans[500005],c[500005];void init(){ q.clear(); mdzz.clear(); memset(c,0,sizeof c);}bool cmp(node A,node B){ if(A.x==B.x) return A.id<B.id; return A.x<B.x;}bool cmp1(node A,node B){ if(A.y==B.y) return A.id<B.id; return A.y<B.y;}int lowbit(int x){ return x&-x;}void update(int x,int val){ for(;x<=mdzz.size();x+=lowbit(x)) c[x]+=val;}int query(int x){ int sum=0; for(;x>0;x-=lowbit(x)) sum+=c[x]; return sum;}void countstar(){ for(int i=0;i<q2.size();i++) { if(q2[i].kind==0) update(q2[i].z,1); else ans[q2[i].id]+=q2[i].kind*query(q2[i].z); } for(int i=0;i<q2.size();i++) { if(q2[i].kind==0) update(q2[i].z,-1); }}void CDQ1(int l,int r){ if(l>=r) return ; int mid=l+r>>1; CDQ1(l,mid); CDQ1(mid+1,r); q2.clear(); for(int i=l;i<=mid;i++) { if(q1[i].kind==0) q2.push_back(q1[i]); } for(int i=mid+1;i<=r;i++) { if(q1[i].kind!=0) q2.push_back(q1[i]); } sort(q2.begin(),q2.end(),cmp1); countstar();}void CDQ(int l,int r){ if(l>=r) return ; int mid=l+r>>1; CDQ(l,mid); CDQ(mid+1,r); q1.clear(); for(int i=l;i<=mid;i++) { if(q[i].kind==0) q1.push_back(q[i]); } for(int i=mid+1;i<=r;i++) { if(q[i].kind!=0) q1.push_back(q[i]); } sort(q1.begin(),q1.end(),cmp); CDQ1(0,q1.size()-1);}int main(){ int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); init(); for(int i=0;i<n;i++) { int op; scanf("%d",&op); if(op==1) { int x,y,z; scanf("%d%d%d",&x,&y,&z); q.push_back(node(x,y,z,0,i)); mdzz.push_back(z); ans[i]=-1; } else { int x1,y1,z1,x2,y2,z2; scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2); x1--,y1--,z1--; q.push_back(node(x1,y1,z1,-1,i)); q.push_back(node(x2,y2,z2,1,i)); q.push_back(node(x1,y2,z2,-1,i)); q.push_back(node(x2,y1,z2,-1,i)); q.push_back(node(x2,y2,z1,-1,i)); q.push_back(node(x2,y1,z1,1,i)); q.push_back(node(x1,y2,z1,1,i)); q.push_back(node(x1,y1,z2,1,i)); mdzz.push_back(z1); mdzz.push_back(z2); ans[i]=0; } } sort(mdzz.begin(),mdzz.end()); mdzz.erase(unique(mdzz.begin(),mdzz.end()),mdzz.end()); for(int i=0;i<q.size();i++) { q[i].z=(lower_bound(mdzz.begin(),mdzz.end(),q[i].z)-mdzz.begin())+1; } CDQ(0,q.size()-1); for(int i=0;i<n;i++) { if(ans[i]!=-1) printf("%d\n",ans[i]); } } return 0;}
- CDQ分治正确的入门姿势
- 树链剖分正确的入门姿势
- 入门TrafficServer插件开发的正确姿势
- CDQ分治入门---蝗灾、Mokia
- 搜索的正确姿势
- 读开源库的正确姿势
- 跑步的正确姿势
- 跑步的正确姿势
- 正确的关机姿势
- 正确的科研姿势
- Android开发入门的正确姿势,你get到了吗?
- cdq分治
- cdq分治
- cdq分治
- cdq分治
- 【圆的反演变换+cdq分治】共点圆
- CDQ-分治和树套树 的结合小结
- 对CDQ分治的一些见解
- 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字
- EntityValidationErrors
- spark环境搭建
- 数据结构实验之查找七:线性之哈希表
- win7建立无线网共享internet连接给其它设备
- CDQ分治正确的入门姿势
- 滤波电容的大小的选取
- 请求转发和请求重定向的区别
- Ubuntu设置允许root用户登录
- Handler实用代码
- 函数和函数闭包
- MySQL中使用load data命令进行数据导入
- php根据某年某月获取该月天数
- Android Studio学习基础篇一