KD-Tree

KD-Tree

写在KD-Tree讲解之前,请先让我评论一番,“这什么垃圾算法!这也太垃圾了!”BY Winniechen

BY GXZlegend KD-Tree,时间复杂度可证的可以被可持久化线段树替代,时间复杂度不可证的...时间复杂度不可证时间复杂度就是能被卡成n^2

但是,就算KD-Tree的时间复杂度是n^2,(卡起来很费劲,基本卡不到...)

怎么说呢,就是n^2的暴搜加剪枝...至于剪枝...(学过暴搜就会的那种...每道题不同,但是相差不大,基本就是设一个估价函数之后瞎写)

而有些剪枝之后的时间复杂度可证,其他不可证的就是没有剪枝那种。

另外,KD-Tree主要难卡的地方是每次将一个区间横着切一刀,之后再纵向切一刀,反正就是各种找中位数之后劈成两半,按照平衡树的存储方式存储,至于树高太高一般的解决方法是暴力重构(发现树高大于一个值之后重新建树...),或者是替罪羊树的重构方式(我不会...)

另外,KD-Tree不一定要按照010101的顺序切...(随机数什么的不也可以嘛...但是我没有写过,目测可行,而且更加卡不掉了...)

这种高级操作一般不会用到,用到更多的是插入导致深度过深之后的重构。

我们先讲一下KD-Tree的建树,每次以一维取中位数,作为这个节点,之后再分别建立左右子树。

中位数不会求?排序nlogn,总时间复杂度:O(nlog^2nK)。等等?只需要中位数?快排啊!O(n)取中位数。算了,好好说话,nth_element(tr+l,tr+m,tr+r+1,cmp);就张这个样子就可以了。具体实现是快排...

插入操作:BST的插入方式...我就不多说了...每次和这个节点的对应d进行比较之后排序就可以了。

查询操作:暴搜+剪枝,我不说了...遍历树+剪枝...

重构操作:找个随便的参数重构即可...(就是再建一遍树)

例题时间:

BZOJ2648: SJY摆棋子 & BZOJ2716: [Violet 3]天使玩偶

分析:KD-Tree入门题...查询曼哈顿距离最小,设一个估价函数(建议手画一下,推一推,也不是很难理解),时间复杂度严格O(nsqrt(n)),跑得比CDQ分治+树状数组快了不少...毕竟每层四个树状数组看的我头皮发麻...

附上代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
#define N 1000005
#define inf 0x3f3f3f3f
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
char buf[100000],*p1,*p2;
inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
inline int rd() {
	register int x=0;register char ch=nc();
	while(ch<'0'||ch>'9') ch=nc();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
	return x;
}
int rot,d,ans,n,Q;
struct KDTree
{
	int ch[2],maxn[2],minn[2],p[2];
	friend bool operator<(const KDTree &a,const KDTree &b)
	{
		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
	}
}tr[N];
#define PushUp(rt,s) tr[rt].minn[1]=min(tr[rt].minn[1],tr[s].minn[1]),tr[rt].minn[0]=min(tr[rt].minn[0],tr[s].minn[0]),tr[rt].maxn[0]=max(tr[rt].maxn[0],tr[s].maxn[0]),tr[rt].maxn[1]=max(tr[rt].maxn[1],tr[s].maxn[1])
int build(int l,int r,int flag)
{
	int m=(l+r)>>1;
	d=flag,nth_element(tr+l,tr+m,tr+r+1);
	tr[m].maxn[0]=tr[m].minn[0]=tr[m].p[0];
	tr[m].maxn[1]=tr[m].minn[1]=tr[m].p[1];
	if(l<m)tr[m].ch[0]=build(l,m-1,flag^1),PushUp(m,tr[m].ch[0]);
	if(m<r)tr[m].ch[1]=build(m+1,r,flag^1),PushUp(m,tr[m].ch[1]);
	return m;
}
inline void insert(int x)
{
	int *rt=&rot;d=0;int tmp=0;
	while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1,tmp++;
	*rt=x;
	if(tmp>=200)rot=build(1,n,0);
}
#define get_dis(rt,x,y) (max(tr[rt].minn[0]-x,0)+max(x-tr[rt].maxn[0],0)+max(tr[rt].minn[1]-y,0)+max(y-tr[rt].maxn[1],0))
void query(int rt,int x,int y)
{
	int dis_rt=abs(x-tr[rt].p[0])+abs(y-tr[rt].p[1]);
	int dis_ls=ls?get_dis(ls,x,y):inf,dis_rs=rs?get_dis(rs,x,y):inf;
	ans=min(ans,dis_rt);
	if(dis_ls<dis_rs)
	{
		if(dis_ls<ans)query(ls,x,y);
		if(dis_rs<ans)query(rs,x,y);
	}else
	{
		if(dis_rs<ans)query(rs,x,y);
		if(dis_ls<ans)query(ls,x,y);
	}
}
int main()
{
	n=rd();Q=rd();
	for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0);
	while(Q--)
	{
		int op=rd(),x=rd(),y=rd();
		if(op==1)n++,tr[n].p[0]=tr[n].maxn[0]=tr[n].minn[0]=x,tr[n].p[1]=tr[n].maxn[1]=tr[n].minn[1]=y,insert(n);
		else ans=inf,query(rot,x,y),printf("%d
",ans);
	}return 0;
}

BZOJ4066: 简单题

分析:如果不强制在线的话CDQ分治搞一搞很可以...但是显然,它强制在线...和上一题一样,时间复杂度可证的O(nsqrt(n)),具体证明我就不写了...查询的时候,类似Splay区间操作的存储方式,存一个子树和即可。

附上代码:

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstring>
using namespace std;
#define N 200005
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
int rot,d,last,n,Q;
struct KDTree
{
	int p[2],ch[2],maxn[2],minn[2],sum,w;
	friend bool operator<(const KDTree &a,const KDTree &b)
	{
		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
	}
}tr[N];
void PushUp(int rt,int s)
{
	tr[rt].sum+=tr[s].sum;
	tr[rt].maxn[0]=max(tr[rt].maxn[0],tr[s].maxn[0]);
	tr[rt].maxn[1]=max(tr[rt].maxn[1],tr[s].maxn[1]);
	tr[rt].minn[1]=min(tr[rt].minn[1],tr[s].minn[1]);
	tr[rt].minn[0]=min(tr[rt].minn[0],tr[s].minn[0]);
}
int build(int l,int r,int flag)
{
	int m=(l+r)>>1,rt=m;d=flag;nth_element(tr+l,tr+m,tr+r+1);tr[m].ch[0]=tr[m].ch[1]=0;
	tr[m].maxn[0]=tr[m].minn[0]=tr[m].p[0];tr[m].maxn[1]=tr[m].minn[1]=tr[m].p[1];tr[m].sum=tr[m].w;
	if(l<m)ls=build(l,m-1,!flag),PushUp(m,ls);if(m<r)rs=build(m+1,r,!flag),PushUp(m,rs);return m;
}
void insert(int x)
{
	int *rt=&rot;d=0;
	while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1;
	*rt=x;
}
int query(int rt,int x1,int y1,int x2,int y2)
{
	if(!rt||tr[rt].maxn[0]<x1||tr[rt].maxn[1]<y1||tr[rt].minn[0]>x2||tr[rt].minn[1]>y2)return 0;
	if(tr[rt].maxn[0]<=x2&&tr[rt].maxn[1]<=y2&&tr[rt].minn[0]>=x1&&tr[rt].minn[1]>=y1)return tr[rt].sum;
	int ret=0;if(tr[rt].p[0]>=x1&&tr[rt].p[1]>=y1&&tr[rt].p[0]<=x2&&tr[rt].p[1]<=y2)ret+=tr[rt].w;
	ret+=query(ls,x1,y1,x2,y2)+query(rs,x1,y1,x2,y2);return ret;
}
int main()
{
	scanf("%*d");
	while(1)
	{
		int op,x,y,z,w;scanf("%d",&op);if(op==3)break;
		scanf("%d%d%d",&x,&y,&z);x^=last,y^=last,z^=last;
		if(op==1)
		{
			tr[++n].sum=tr[n].w=z;
			tr[n].p[0]=tr[n].maxn[0]=tr[n].minn[0]=x;
			tr[n].p[1]=tr[n].maxn[1]=tr[n].minn[1]=y;
			insert(n);if(n%10000==0)rot=build(1,n,0);
		}
		else scanf("%d",&w),w^=last,printf("%d
",last=query(rot,x,y,z,w));
	}return 0;
}

23333,跑不过CDQ分治...

BZOJ1941: [Sdoi2010]Hide and Seek

分析:KD-Tree基础题,两个估价函数,分别对应曼哈顿距离最大和最小

附上代码:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <bitset>
using namespace std;
#define N 500005
#define inf 2147483647
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define clear(rt) ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1]
int rot,d,n,ans,ans1,ans2;
char buf[100000],*p1,*p2;
__attribute__((optimize("-O3")))inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
__attribute__((optimize("-O3")))inline int rd() {
	register int x=0;register char ch=nc();
	while(ch<'0'||ch>'9') ch=nc();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
	return x;
}
struct KDTree{int ch[2],p[2],mx[2],mn[2];
	friend bool operator<(const KDTree &a,const KDTree &b){return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];}}tr[N];
#define PushUp(rt,s) tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]),tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1])
__attribute__((optimize("-O3")))int build(int l,int r,int flag)
{
	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
}
#define get_dis_min(rt,x,y) (max(tr[rt].mn[0]-x,0)+max(x-tr[rt].mx[0],0)+max(tr[rt].mn[1]-y,0)+max(y-tr[rt].mx[1],0))
#define get_dis_max(rt,x,y) (max(x-tr[rt].mn[0],0)+max(tr[rt].mx[0]-x,0)+max(y-tr[rt].mn[1],0)+max(tr[rt].mx[1]-y,0))
__attribute__((optimize("-O3")))void query_min(int rt,int x,int y)
{
	int dis_rt=abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y),dis_ls=ls?get_dis_min(ls,x,y):inf,dis_rs=rs?get_dis_min(rs,x,y):inf;if(dis_rt)ans1=min(dis_rt,ans1);
	if(dis_ls<dis_rs){if(dis_ls<ans1)query_min(ls,x,y);if(dis_rs<ans1)query_min(rs,x,y);}else{if(dis_rs<ans1)query_min(rs,x,y);if(dis_ls<ans1)query_min(ls,x,y);}
}
__attribute__((optimize("-O3")))void query_max(int rt,int x,int y)
{
	int dis_rt=abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y),dis_ls=ls?get_dis_max(ls,x,y):-inf,dis_rs=rs?get_dis_max(rs,x,y):-inf;ans2=max(dis_rt,ans2);
	if(dis_ls>dis_rs){if(dis_ls>ans2)query_max(ls,x,y);if(dis_rs>ans2)query_max(rs,x,y);}else{if(dis_rs>ans2)query_max(rs,x,y);if(dis_ls>ans2)query_max(ls,x,y);}
}
__attribute__((optimize("-O3")))int main()
{
	n=rd();ans=inf;
	for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0);
	for(int i=1;i<=n;i++){ans1=inf,ans2=-inf;query_min(rot,tr[i].p[0],tr[i].p[1]);query_max(rot,tr[i].p[0],tr[i].p[1]);ans=min(ans2-ans1,ans);}
	printf("%d
",ans);return 0;
}

BZOJ2850: 巧克力王国

分析:说实话,这题KD-Tree的时间复杂度是可证的n^2...正解给的是似乎是半平面交+整体二分,显然我并不会...GXZlegend出的数据卡的我头皮发麻...不过,数据这么水就当是练一练KD-Tree了。

附上代码:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <bitset>
using namespace std;
#define N 50005
#define ll long long
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define clear(rt) tr[rt].sum=tr[rt].w,ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1]
int d,rot,n,Q;ll a,b,c;
struct KDTree
{
	int mx[2],p[2],ch[2],mn[2],w;ll sum;
	friend bool operator<(const KDTree &a,const KDTree &b)
	{
		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
	}
}tr[N];
#define PushUp(rt,s) tr[rt].sum+=tr[s].sum,tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]),tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1])
int build(int l,int r,int flag)
{
	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
}
#define check(rt) (((a*tr[rt].mx[0]+b*tr[rt].mx[1])<c)+((a*tr[rt].mn[0]+b*tr[rt].mx[1])<c)+((a*tr[rt].mx[0]+b*tr[rt].mn[1])<c)+((a*tr[rt].mn[0]+b*tr[rt].mn[1])<c))
ll query(int rt)
{
	int tmp;if(!rt||!(tmp=check(rt)))return 0;if(tmp==4)return tr[rt].sum;
	ll ret=0;if(((ll)tr[rt].p[0]*a+(ll)tr[rt].p[1]*b)<c)ret+=tr[rt].w;
	return ret+query(ls)+query(rs);
}
int main()
{
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++)scanf("%d%d%d",&tr[i].p[0],&tr[i].p[1],&tr[i].w);rot=build(1,n,0);
	while(Q--)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		printf("%lld
",query(rot));
	}return 0;
}

BZOJ3489: A simple rmq problem

分析:这题模型很好,考虑如果存在是什么情况,也就是上一次出现在l左边,下一次出现在r右边,那么将每一个a[i]建成一个三维的点,之后查询一个长方体内的点数,这题时间复杂度还是可证的...当然,如果不强制在线的话,我会考虑CDQ分治什么的...

附上代码:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <bitset>
using namespace std;
#define N 100005
#define inf 2147483647
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define clear(rt) for(int i=0;i<3;i++)tr[rt].mx[i]=tr[rt].mn[i]=tr[rt].p[i];
int rot,d,n,Q,ans,p[N],nxt[N],pre[N],a[N];
char buf[100000],*p1,*p2;
inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
inline int rd() {
	register int x=0;register char ch=nc();
	while(ch<'0'||ch>'9') ch=nc();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
	return x;
}
struct KDTree
{
	int p[3],ch[2],mx[3],mn[3],maxx,w;
	friend bool operator<(const KDTree &a,const KDTree &b)
	{
		return a.p[d]==b.p[d]?(a.p[(d+1)%3]==b.p[(d+1)%3]?a.p[(d+2)%3]<b.p[(d+2)%3]:a.p[(d+1)%3]<b.p[(d+1)%3]):a.p[d]<b.p[d];
	}
}tr[N];
void PushUp(int rt,int s)
{
	for(int i=0;i<3;i++)
	{
		tr[rt].mx[i]=max(tr[rt].mx[i],tr[s].mx[i]);
		tr[rt].mn[i]=min(tr[rt].mn[i],tr[s].mn[i]);
	}tr[rt].maxx=max(tr[rt].maxx,tr[s].maxx);
} 
int build(int l,int r,int flag)
{
	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);for(int i=0;i<3;i++)tr[rt].mx[i]=tr[rt].mn[i]=tr[rt].p[i];
	if(rt>l)ls=build(l,rt-1,(flag+1)%3),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,(flag+1)%3),PushUp(rt,rs);return rt;
}
int check(int rt,int x,int y)
{
	if(tr[rt].maxx<=ans||tr[rt].mx[0]<x||tr[rt].mn[0]>y||tr[rt].mn[1]>=x||tr[rt].mx[2]<=y)return 0;
	return 1;
}
void query(int rt,int x,int y)
{
	if(!rt||!check(rt,x,y))return ;
	if(tr[rt].p[0]>=x&&tr[rt].p[0]<=y&&tr[rt].p[1]<x&&tr[rt].p[2]>y)ans=max(ans,tr[rt].w);
	query(ls,x,y);query(rs,x,y);
}
int main()
{
	n=rd();Q=rd();
	for(int i=1,x;i<=n;i++)
	{
		x=rd();pre[i]=p[x];a[i]=x;
		if(p[x])nxt[p[x]]=i;
		p[x]=i;
	}
	for(int i=1;i<=n;i++)if(!nxt[i])nxt[i]=n+1;
	for(int i=1;i<=n;i++)tr[i].p[0]=i,tr[i].p[1]=pre[i],tr[i].p[2]=nxt[i],tr[i].w=tr[i].maxx=a[i];
	rot=build(1,n,0);
	while(Q--)
	{
		int x=rd(),y=rd();
		x=(x+ans)%n+1,y=(y+ans)%n+1;
		if(x>y)swap(x,y);
		ans=0;query(rot,x,y);
		printf("%d
",ans);
	}return 0;
}

BZOJ2989: 数列 & BZOJ4170: 极光

分析:目测需要旋转坐标系...毕竟一个斜正方形的查询时间复杂度是不可证的,据说是O(n^2)的,但是呢,我就试了试,AC快乐!查询类似线段树的查询方式和4066一样,反正就是那么一个姿势...

附上代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
#define N 100005
#define inf 0x3f3f3f3f
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define clear(rt) ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1],tr[rt].sum=tr[rt].w;
char buf[1000000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
__attribute__((optimize("-O3")))inline int rd() {
	register int x=0;register char ch=nc();
	while(ch<'0'||ch>'9') ch=nc();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
	return x;
}
int n,d,Q,k,a[N],rot;
struct KDtree
{
	int ch[2],mx[2],mn[2],p[2],sum,w;
	friend bool operator<(const KDtree &a,const KDtree &b)
	{
		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
	}
}tr[N];
void PushUp(int rt,int s)
{
	tr[rt].sum+=tr[s].sum;
	tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]);
	tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]);
	tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]);
	tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]);
}
int build(int l,int r,int flag)
{
	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
}
void insert(int x)
{
	int *rt=&rot;d=0;
	while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1;
	*rt=x;
}
#define get_dis_min(rt,x,y) (max(tr[rt].mn[0]-x,0)+max(x-tr[rt].mx[0],0)+max(tr[rt].mn[1]-y,0)+max(y-tr[rt].mx[1],0))
#define get_dis_max(rt,x,y) (max(x-tr[rt].mn[0],0)+max(tr[rt].mx[0]-x,0)+max(y-tr[rt].mn[1],0)+max(tr[rt].mx[1]-y,0))
int query(int rt,int x,int y)
{
	if(!rt||get_dis_min(rt,x,y)>k)return 0;
	if(get_dis_max(rt,x,y)<=k)return tr[rt].sum;int ret=0;
	if(abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y)<=k)ret+=tr[rt].w;
	return ret+query(ls,x,y)+query(rs,x,y);
}char s[10];
int main()
{
	scanf("%d%d",&n,&Q);
	for(int i=1,x;i<=n;i++)scanf("%d",&x),tr[i].p[0]=i,tr[i].p[1]=a[i]=x,tr[i].w=1;rot=build(1,n,0);
	while(Q--)
	{
		int x,y;scanf("%s%d%d",s,&x,&y);
		if(s[0]=='Q')k=y,printf("%d
",query(rot,x,a[x]));
		else
		{
			tr[++n].p[0]=tr[n].mx[0]=tr[n].mn[0]=x;
			tr[n].p[1]=tr[n].mx[1]=tr[n].mn[1]=y;
			tr[n].w=tr[n].sum=1;a[x]=y;insert(n);
		}
	}
	return 0;
}

BZOJ4520: [Cqoi2016]K远点对 & BZOJ2626: JZPFAR

分析:考虑暴力怎么求,维护一个有K个元素的小根堆,之后枚举n^2压入和弹出即可。那么KD-Tree干什么呢?剪枝!类似曼哈顿距离最大...只是变成了和堆顶比较...

附上代码:

4520:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <iostream>
#include <cstdlib>
using namespace std;
#define N 100005
#define inf (1ll<<60)
#define ll long long
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define squ(x) ((ll)(x)*(x))
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define clear(rt) (tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1])
#define PushUp(rt,s) tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]),tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0])
#define get_dis(rt,x,y) (squ(tr[rt].p[0]-x)+squ(tr[rt].p[1]-y))
#define pre_dis(rt,x,y) (max(squ(tr[rt].mn[0]-x),squ(tr[rt].mx[0]-x))+max(squ(tr[rt].mn[1]-y),squ(tr[rt].mx[1]-y)))
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
char buf[1000000],*p1,*p2;
__attribute__((optimize("-O3")))inline int rd()
{
	register int x=0;register char ch=nc();
	while(ch<'0'||ch>'9') ch=nc();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
	return x;
}
int d,rot,n,k;__attribute__((optimize("-O3")))priority_queue<ll>q;
struct KDtree
{
	int ch[2],p[2],mx[2],mn[2];
	__attribute__((optimize("-O3")))friend bool operator<(const KDtree &a,const KDtree &b)
	{
		return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];
	}
}tr[N];
__attribute__((optimize("-O3")))int build(int l,int r,int flag)
{
	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt);
	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
}
__attribute__((optimize("-O3")))void query(int rt,int x,int y)
{
	ll dis_rt=get_dis(rt,x,y),dis_ls=ls?pre_dis(ls,x,y):-inf,dis_rs=rs?pre_dis(rs,x,y):-inf;
	if(-q.top()<dis_rt)q.pop(),q.push(-dis_rt);
	if(dis_ls>dis_rs){if(dis_ls>-q.top())query(ls,x,y);if(dis_rs>-q.top())query(rs,x,y);}
	else{if(dis_rs>-q.top())query(rs,x,y);if(dis_ls>-q.top())query(ls,x,y);}
}
__attribute__((optimize("-O3")))int main()
{
	n=rd();k=rd();k=k<<1;
	for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0);
	for(int i=1;i<=k;i++)q.push(0);for(int i=1;i<=n;i++)query(rot,tr[i].p[0],tr[i].p[1]);
	printf("%lld
",-q.top());
}

2626:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
#define N 100005
#define inf (1ll<<60)
#define ll long long
#define ls tr[rt].ch[0]
#define rs tr[rt].ch[1]
#define squ(x) ((ll)(x)*(x))
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
#define clear(rt) (tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1])
#define PushUp(rt,s) tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]),tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0])
#define get_dis(rt,x,y) (squ(tr[rt].p[0]-x)+squ(tr[rt].p[1]-y))
#define pre_dis(rt,x,y) (max(squ(tr[rt].mn[0]-x),squ(tr[rt].mx[0]-x))+max(squ(tr[rt].mn[1]-y),squ(tr[rt].mx[1]-y)))
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
char buf[1000000],*p1,*p2;
__attribute__((optimize("-O3")))inline int rd()
{
	register int x=0,f=1;register char ch=nc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=nc();
	return x*f;
}
int d,rot,n,Q;priority_queue<pair<ll,int> >q;
struct KDtree{int p[2],mn[2],mx[2],idx,ch[2];}tr[N];
__attribute__((optimize("-O3")))bool cmp(const KDtree &a,const KDtree &b){return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];}
__attribute__((optimize("-O3")))int build(int l,int r,int flag)
{
	int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1,cmp);clear(rt);
	if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt;
}
__attribute__((optimize("-O3")))void query(int rt,int x,int y)
{
	ll dis_rt=get_dis(rt,x,y),dis_ls=ls?pre_dis(ls,x,y):-inf,dis_rs=rs?pre_dis(rs,x,y):-inf;
	q.push(make_pair(-dis_rt,tr[rt].idx));q.pop();
	if(dis_ls>dis_rs){if(dis_ls>=-q.top().first)query(ls,x,y);if(dis_rs>=-q.top().first)query(rs,x,y);}
	else {if(dis_rs>=-q.top().first)query(rs,x,y);if(dis_ls>=-q.top().first)query(ls,x,y);}
}
__attribute__((optimize("-O3")))int main()
{
	n=rd();for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd(),tr[i].idx=i;rot=build(1,n,0);
	Q=rd();while(Q--)
	{
		int x=rd(),y=rd(),z=rd();while(!q.empty())q.pop();
		for(int i=1;i<=z;i++)q.push(make_pair(0,1<<30));
		query(rot,x,y);printf("%d
",q.top().second);
	}return 0;
}

那么先讲到这里吧...毕竟什么崂山白水我也不会...替罪羊树我也不会...我也就没有写...得了得了...不多说了,去做KD-Tree的课件了...过不了多久就会上传的!

转载于:https://www.cnblogs.com/Winniechen/p/9274473.html

原文地址:https://www.cnblogs.com/twodog/p/12136544.html