看到题面,第一眼想到的是用cdq或树状数组维护这东东。
但是强制在线限制了我们的想象
然后想到用树套树,毕竟时间和空间好像是 O ( n log 2 n ) O(n\log^2n) O(nlog2n)的。
但是20M的空间限制了我们的想象
那么我们就会用到 k d − t r e e kd-tree kd−tree来维护。
1. k d − t r e e kd-tree kd−tree是什么
k d − t r e e kd-tree kd−tree其实是一颗平衡树(替罪羊树),它维护了 k k k维空间的 n n n个点的信息。
2.如何将一个点插入 k d − t r e e kd-tree kd−tree
我们对于深度为 i i i的位置,我们按照 i % k i\%k i%k维的坐标来比较,然后按普通平衡树的插入即可。
比如,不妨设这个点是 A ( x 0 , x 1 , . . . , x k − 1 ) A(x_0,x_1,...,x_{k-1}) A(x0,x1,...,xk−1),我们插入时第一个要比较的节点是 u = r o o t u=root u=root,那么如果 x 0 ≤ u . x 0 x_0\leq u.x_0 x0≤u.x0( u . x 0 u.x_0 u.x0即为 u u u这个点的第 0 0 0维的坐标),那么我们跳到 u u u的左子树去,即 u = c h [ u ] [ 0 ] u=ch[u][0] u=ch[u][0]。然后在让 x 1 x_1 x1和当前 u u u的 x 1 x_1 x1比较,如此推类,直到找到一个空节点截止。
如果是建树也同理,在深度为 i i i的位置,我们按照 i % k i\%k i%k维的坐标比较,找出当前点集中的点的第 i % k i\%k i%k维的坐标中在最中间的这一个点。通俗的来说,就是将当前点集按第 i % k i\%k i%k维的坐标排一遍序,然后取出最中间的点,把它当成当前子树的根,再向左右儿子遍历。
如何更好地理解上述算法的正确性或过程?
看图:(这里以2维为例)
假设现在坐标系里有这么一些点:
我们按照刚才的方法,先按第一维进行比较,得出最中间的点 A A A。
那么我们把 A A A当作当前树的根,把 B B B、 F F F、 C C C扔进左子树,把 D D D、 G G G、 I I I扔进右子树。
几何意义上就是做一个以 A A A为中心点在第一维的分割:
同理,在进入下一层的建树时,我们按第2维来比较。
几何意义:
然后第三层,我们按第一维比较:
这样下来就分割完了,然后建出来的树长这样:
那么在比如我们要插入一个点 J ( 3.5 , 4.5 ) J(3.5,4.5) J(3.5,4.5)
我们先让 J J J的横坐标与当前点 A A A的横坐标比较,发现 x J > x A x_J>x_A xJ>xA,所以继续递归 A A A的右子树 G G G。
然后再让 J J J的纵坐标与当前点 G G G的纵坐标比较,发现 y J < y G y_J<y_G yJ<yG,所以继续递归 G G G的左子树 D D D。
然后再让 J J J的横坐标与当前点 D D D的横坐标比较,发现 x J < x D x_J<x_D xJ<xD,所以继续递归 D D D的左子树。
发现当前节点是空节点,我们就把当前节点设为 J J J。
然后树就变成了这样:
然后记得如果某棵树不平衡,就暴力重构。
这就是整个 k d − t r e e kd-tree kd−tree插入和建树的全过程。
至于查询,由于在几何意义上,我们每棵子树都代表的是一个矩形,所以我们需要维护这个矩形的 m i n x min_x minx、 m a x x max_x maxx、 m i n y min_y miny、 m a x y max_y maxy,也就是矩形的左边界,右边界,上边界和下边界。
然后我们就可以用这些信息搞事情了。
比如对于这道题,如果你当前子树维护的矩形完全在询问的矩形,就直接返回当前矩形内所有点的权值和就好了,否则就继续递归。
完整代码和注释如下:
#include<bits/stdc++.h>
#define N 500010
#define M 200010
#define lc t[u].ch[0]
#define rc t[u].ch[1]
using namespace std;
struct Point
{
int num[2],val;//num[0]即x,num[1]即y
Point(){};
Point(int xx,int yy,int vall)
{
num[0]=xx,num[1]=yy,val=vall;
}
}p[M];
struct kd_tree
{
int ch[2],minn[2],maxn[2],sum,size;//minn[0]即minx,minn[1]即miny,maxn[0]即maxx,maxn[1]即maxy
Point p;
}t[M];
const double alpha=0.75;
int fuck;
int n,tot,root;
int cnt,rubbish[M];
bool cmp0(Point a,Point b)
{
return a.num[0]<b.num[0];
}
bool cmp1(Point a,Point b)
{
return a.num[1]<b.num[1];
}
int newnode()
{
if(cnt)return rubbish[cnt--];
return ++tot;
}
void up(int u)
{
for(int i=0;i<2;i++)
{
t[u].minn[i]=t[u].maxn[i]=t[u].p.num[i];
if(lc)
{
t[u].minn[i]=min(t[u].minn[i],t[lc].minn[i]);
t[u].maxn[i]=max(t[u].maxn[i],t[lc].maxn[i]);
}
if(rc)
{
t[u].minn[i]=min(t[u].minn[i],t[rc].minn[i]);
t[u].maxn[i]=max(t[u].maxn[i],t[rc].maxn[i]);
}
}
t[u].sum=t[lc].sum+t[u].p.val+t[rc].sum;
t[u].size=t[lc].size+t[rc].size+1;
}
void slap(int u)//把所有的点扔进一个数组里
{
if(!u) return;
p[++fuck]=t[u].p;
rubbish[++cnt]=u;
slap(lc);
slap(rc);
}
int rebuild(int l,int r,int d)//暴力重构
{
if(l>r) return 0;
int mid=(l+r)>>1,u=newnode();
nth_element(p+l,p+mid,p+r+1,d?cmp1:cmp0);
t[u].p=p[mid];
lc=rebuild(l,mid-1,d^1);
rc=rebuild(mid+1,r,d^1);
up(u);
return u;
}
void check(int &u,int d)//检查是否不平衡
{
if(t[lc].size>alpha*t[u].size||t[rc].size>alpha*t[u].size)
{
fuck=0;
slap(u);
u=rebuild(1,t[u].size,d);
}
}
void insert(int &u,Point now,int d)
{
if(!u)
{
u=newnode();
lc=rc=0,t[u].p=now;
up(u);
return;
}
if(now.num[d]<=t[u].p.num[d]) insert(lc,now,d^1);//按照第d维的坐标比较
else insert(rc,now,d^1);
up(u);
check(u,d);
}
bool inside(int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2)
{
return x1<=X1&&x2>=X2&&y1<=Y1&&y2>=Y2;
}
bool outside(int x1,int y1,int x2,int y2,int X1,int Y1,int X2,int Y2)
{
return x1>X2||x2<X1||y1>Y2||y2<Y1;
}
int query(int u,int x1,int y1,int x2,int y2)
{
if(!u) return 0;
if(inside(x1,y1,x2,y2,t[u].minn[0],t[u].minn[1],t[u].maxn[0],t[u].maxn[1])) //判断当前矩形是否完全在询问矩形内
return t[u].sum;
if(outside(x1,y1,x2,y2,t[u].minn[0],t[u].minn[1],t[u].maxn[0],t[u].maxn[1])) //判断当前矩形是否完全在询问矩形外
return 0;
int ans=0;
if(inside(x1,y1,x2,y2,t[u].p.num[0],t[u].p.num[1],t[u].p.num[0],t[u].p.num[1])) //如果根在询问矩形内
ans+=t[u].p.val;
ans+=query(lc,x1,y1,x2,y2)+query(rc,x1,y1,x2,y2);//统计左右子树的答案
return ans;
}
int main()
{
scanf("%d",&n);
int opt,lastans=0;
while(scanf("%d",&opt))
{
if(opt==1)
{
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
x^=lastans,y^=lastans,val^=lastans;
insert(root,Point(x,y,val),0);
}
if(opt==2)
{
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1^=lastans,y1^=lastans,x2^=lastans,y2^=lastans;
printf("%d\n",lastans=query(root,x1,y1,x2,y2));
}
if(opt==3) break;
}
}