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 0x3f3f3f3f
struct 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;
}