Kd-tree

基操

解决K维的一般数据结构能解决的问题:

  1. 求点距离(各种意义下的),其中如果是\(|x_i-x_j|+|y_i-y_j|\)可以化掉绝对值用树状数组维护
  2. 修改+查询(直接dfs,把所有能想到的剪枝都加上去)

模板

相当于维护了一棵平衡树?

Luogu P4169 [Violet]天使玩偶/SJY摆棋子

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

inline int rd() {
	int ret=0; char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
	return ret;
}

const int N=1e6+5;
int n,m,ls[N],rs[N],sz[N],c[N],qx,qy,ans,rt;
struct A{
	int x,y,l[2],r[2],w;
}a[N];

inline bool cmpx(int i,int j) {
	return a[i].x<a[j].x;
}
inline bool cmpy(int i,int j) {
	return a[i].y<a[j].y;
}
inline void up(int p) {
	sz[p]=sz[ls[p]]+sz[rs[p]]+1; 
	a[p].l[0]=a[p].r[0]=a[p].x;
	a[p].l[1]=a[p].r[1]=a[p].y;
	if(ls[p]) {
		a[p].l[0]=min(a[p].l[0],a[ls[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[ls[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[ls[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[ls[p]].r[1]);
	}	
	if(rs[p]) {
		a[p].l[0]=min(a[p].l[0],a[rs[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[rs[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[rs[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[rs[p]].r[1]);
	}
}
inline void bld(int &p,int l,int r) {
	if(l>r) {
		p=0; return;
	}
	int mid=l+r>>1;
	double a0=0,a1=0,b0=0,b1=0;
	for(int i=l;i<=r;i++) {
		a0+=a[c[i]].x,b0+=a[c[i]].y;
	}
	a0/=(r-l+1),b0/=r-l+1;
	for(int i=l;i<=r;i++) {
		a1+=(a[c[i]].x-a0)*(a[c[i]].x-a0);
		b1+=(a[c[i]].y-b0)*(a[c[i]].y-b0);
	}
	if(a1>b1) {
		nth_element(c+l,c+mid,c+r+1,cmpx);
		p=c[mid]; a[p].w=0;	
	} else {
		nth_element(c+l,c+mid,c+r+1,cmpy);
		p=c[mid]; a[p].w=1;	
	}
	bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
	up(p);
}

int tot;
queue<int>Q; 
inline void rebld(int &p) {
	tot=0;
	Q.push(p); int u;
	while(!Q.empty()) {
		u=Q.front(); Q.pop();
		c[++tot]=u;
		if(ls[u]) Q.push(ls[u]);
		if(rs[u]) Q.push(rs[u]);
	}
	bld(p,1,tot);
}
void ins(int &p,int x) {
	if(!p) {
		p=x; up(p); return;	
	}
	if(a[p].w==0) {
		if(a[p].x>=a[x].x) ins(ls[p],x);
			else ins(rs[p],x);
	} else {
		if(a[p].y>=a[x].y) ins(ls[p],x);
			else ins(rs[p],x);
	}
	up(p);
	if(0.75*sz[p]<=max(sz[ls[p]],sz[rs[p]])) {
		rebld(p);
	}
}
inline int gz(int p) {
	return max(0,a[p].l[0]-qx)+max(0,qx-a[p].r[0])+max(0,a[p].l[1]-qy)+max(0,qy-a[p].r[1]);
}
void ask(int p) {
	ans=min(ans,abs(a[p].x-qx)+abs(a[p].y-qy));
	int x=INF,y=INF;
	if(ls[p]) x=gz(ls[p]);
	if(rs[p]) y=gz(rs[p]);
	if(x<y) {
		if(x<ans) ask(ls[p]);
		if(y<ans) ask(rs[p]);
	} else {
		if(y<ans) ask(rs[p]);
		if(x<ans) ask(ls[p]);
	}
}
int main(){
	n=rd(); int T=rd();
	for(int i=1;i<=n;i++) {
		c[i]=i; a[i].x=rd(),a[i].y=rd();
	}
	bld(rt,1,n);
	while(T--) {
		int op=rd();
		if(op==1) {
			a[++n].x=rd(),a[n].y=rd();
			ins(rt,n);
		} else {
			qx=rd(),qy=rd();
			ans=INF;
			ask(rt);
			printf("%d\n",ans);
		}
	}
	return 0;
}

线段树上的节点标号表示在序列中的编号,c[i]表示在序列中的编号

注意:插入后要及时up,插入中也要up,bld中也要up,不要忘记

例题1

Luogu P2093 [国家集训队]JZPFAR

用堆维护

注意求K远点,估值函数需要调整

#include<bits/stdc++.h>
#define ll long long
using namespace std;

inline int rd() {
	int ret=0; char ch=getchar(); bool fl=0;
	for(;!isdigit(ch);ch=getchar()) fl=(ch=='-');
	for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
	return fl?-ret:ret;
}

const int N=1e6+5;
int n,m,ls[N],rs[N],c[N],qx,qy,ans,rt;
struct A{
	int x,y,l[2],r[2],w;
}a[N];
struct B{int id; ll x; };
bool operator >(B i,B j) {
	return i.x>j.x||i.x==j.x&&i.id<j.id;
}
priority_queue<B,vector<B>,greater<B> >q; 
inline bool cmpx(int i,int j) {
	return a[i].x<a[j].x;
}
inline bool cmpy(int i,int j) {
	return a[i].y<a[j].y;
}
inline void up(int p) {
	a[p].l[0]=a[p].r[0]=a[p].x;
	a[p].l[1]=a[p].r[1]=a[p].y;
	if(ls[p]) {
		a[p].l[0]=min(a[p].l[0],a[ls[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[ls[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[ls[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[ls[p]].r[1]);
	}	
	if(rs[p]) {
		a[p].l[0]=min(a[p].l[0],a[rs[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[rs[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[rs[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[rs[p]].r[1]);
	}
}
inline void bld(int &p,int l,int r) {
	if(l>r) {
		p=0; return;
	}
	int mid=l+r>>1;
	double a0=0,a1=0,b0=0,b1=0;
	for(int i=l;i<=r;i++) {
		a0+=a[c[i]].x,b0+=a[c[i]].y;
	}
	a0/=(r-l+1),b0/=r-l+1;
	for(int i=l;i<=r;i++) {
		a1+=(a[c[i]].x-a0)*(a[c[i]].x-a0);
		b1+=(a[c[i]].y-b0)*(a[c[i]].y-b0);
	}
	if(a1>b1) {
		nth_element(c+l,c+mid,c+r+1,cmpx);
		p=c[mid]; a[p].w=0;	
	} else {
		nth_element(c+l,c+mid,c+r+1,cmpy);
		p=c[mid]; a[p].w=1;	
	}
	bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
	up(p);
}

inline ll gz(int p) {
	return (ll)max(0,a[p].r[0]-qx)*max(0,a[p].r[0]-qx)+(ll)max(0,qx-a[p].l[0])*max(0,qx-a[p].l[0])+(ll)max(0,a[p].r[1]-qy)*max(0,a[p].r[1]-qy)+(ll)max(0,qy-a[p].l[1])*max(0,qy-a[p].l[1]);
}
void ask(int p) {
	ll D=(ll)(qx-a[p].x)*(qx-a[p].x)+(ll)(qy-a[p].y)*(qy-a[p].y);
	if(q.top().x<D||q.top().x==D&&p<q.top().id) {
		q.pop();
		q.push((B){p,D});
	}
	ll x=-2,y=-2;
	if(ls[p]) x=gz(ls[p]);
	if(rs[p]) y=gz(rs[p]);
	if(x>y) {
		if(x>=q.top().x) ask(ls[p]);
		if(y>=q.top().x) ask(rs[p]);
	} else {
		if(y>=q.top().x) ask(rs[p]);
		if(x>=q.top().x) ask(ls[p]);
	}
}
int main(){
	n=rd();
	for(int i=1;i<=n;i++) {
		c[i]=i; a[i].x=rd(),a[i].y=rd();
	}
	bld(rt,1,n);
	int T=rd();
	while(T--) {
		qx=rd(),qy=rd(); int k=rd();
		for(int i=1;i<=k;i++) q.push((B){0,-1});
		ask(rt);
		printf("%d\n",q.top().id);
		for(int i=1;i<=k;i++) q.pop();
	}
	return 0;
}

例题2

Luogu P4148 简单题

把二维的点当作点

尝试最优的dfs即可(剪枝越多越好)

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

inline int rd() {
	int ret=0; char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
	return ret;
}

const int N=2e5+5;
int n,m,ls[N],rs[N],sz[N],c[N],qx,qy,k,ans,rt;
struct A{
	int x,y,l[2],r[2],w,s,c;
}a[N];

inline bool cmpx(int i,int j) {
	return a[i].x<a[j].x;
}
inline bool cmpy(int i,int j) {
	return a[i].y<a[j].y;
}
inline void up(int p) {
	sz[p]=sz[ls[p]]+sz[rs[p]]+1; 
	a[p].s=a[ls[p]].s+a[rs[p]].s+a[p].c;
	a[p].l[0]=a[p].r[0]=a[p].x;
	a[p].l[1]=a[p].r[1]=a[p].y;
	if(ls[p]) {
		a[p].l[0]=min(a[p].l[0],a[ls[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[ls[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[ls[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[ls[p]].r[1]);
	}	
	if(rs[p]) {
		a[p].l[0]=min(a[p].l[0],a[rs[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[rs[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[rs[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[rs[p]].r[1]);
	}
}
inline void bld(int &p,int l,int r) {
	if(l>r) {
		p=0; return;
	}
	int mid=l+r>>1;
	double a0=0,a1=0,b0=0,b1=0;
	for(int i=l;i<=r;i++) {
		a0+=a[c[i]].x,b0+=a[c[i]].y;
	}
	a0/=(r-l+1),b0/=r-l+1;
	for(int i=l;i<=r;i++) {
		a1+=(a[c[i]].x-a0)*(a[c[i]].x-a0);
		b1+=(a[c[i]].y-b0)*(a[c[i]].y-b0);
	}
	if(a1>b1) {
		nth_element(c+l,c+mid,c+r+1,cmpx);
		p=c[mid]; a[p].w=0;	
	} else {
		nth_element(c+l,c+mid,c+r+1,cmpy);
		p=c[mid]; a[p].w=1;	
	}
	bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
	up(p);
}

int tot,u;
queue<int>Q; 
inline void rebld(int &p) {
	tot=0,Q.push(p); 
	while(!Q.empty()) {
		u=Q.front(); Q.pop();
		c[++tot]=u;
		if(ls[u]) Q.push(ls[u]);
		if(rs[u]) Q.push(rs[u]);
	}
	bld(p,1,tot);
}
void ins(int &p) {
	if(!p) {
		a[++n].x=qx,a[n].y=qy,a[n].c=k,p=n; up(p); 
		return;	
	}
	if(a[p].x==qx&&a[p].y==qy) {
		a[p].c+=k; up(p);
		return;
	}
	if(a[p].w==0) {
		if(a[p].x>=qx) ins(ls[p]);
			else ins(rs[p]);
	} else {
		if(a[p].y>=qy) ins(ls[p]);
			else ins(rs[p]);
	}
	up(p);
	if(0.75*sz[p]<=max(sz[ls[p]],sz[rs[p]])) {
		rebld(p);
	}
}
int x1,Y1,x2,y2;
void ask(int p) {
	if(x1>a[p].r[0]||x2<a[p].l[0]||Y1>a[p].r[1]||y2<a[p].l[1]) {
		return;
	}
	if(x1<=a[p].l[0]&&a[p].r[0]<=x2&&Y1<=a[p].l[1]&&a[p].r[1]<=y2) {
		ans+=a[p].s; return;
	}
	if(x1<=a[p].x&&a[p].x<=x2&&Y1<=a[p].y&&a[p].y<=y2) {
		ans+=a[p].c;
	}
	ask(ls[p]),ask(rs[p]);
}
int main(){
	rd(); int op;
	while((op=rd())!=3) {
		if(op==1) {
			qx=rd()^ans,qy=rd()^ans,k=rd()^ans;
			ins(rt);
		} else {
			x1=rd()^ans,Y1=rd()^ans,x2=rd()^ans,y2=rd()^ans;
			ans=0; ask(rt);
			printf("%d\n",ans);
		}
	}
	return 0;
}

注意:y1不能为变量,好像是C++自带的指针?

例题3

ybtoj 染颜色

子树,可以看作\(dfn[u]\in[dfn[v],dfn[v]+sz[v])\)

不超过\(l\),可以看作\(dep[u]\in[dep[v],dep[v]+l]\)

所以可以将每个点看作\((dfn[u],dep[u])\),二维修改,二维查询

树套树空间不允许,所以kd-tree

#include<bits/stdc++.h>
#define ll long long
const int INF=1e9;
const int P=1e9+7;
using namespace std;

inline int rd() {
	int ret=0; char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	for(;isdigit(ch);ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
	return ret;
}

const int N=1e5+5;
int n,m,ls[N],rs[N],sz[N],c[N],rt;
struct A{
	int x,y,c,l[2],r[2],w,t;
}a[N];
vector<int>V[N];

inline bool cmpx(int i,int j) {
	return a[i].x<a[j].x||a[i].x==a[j].x&&a[i].y<a[j].y;
}
inline bool cmpy(int i,int j) {
	return a[i].y<a[j].y||a[i].y==a[j].y&&a[i].x<a[j].x;
}
inline void up(int p) {
	a[p].l[0]=a[p].r[0]=a[p].x;
	a[p].l[1]=a[p].r[1]=a[p].y;
	if(ls[p]) {
		a[p].l[0]=min(a[p].l[0],a[ls[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[ls[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[ls[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[ls[p]].r[1]);
	}	
	if(rs[p]) {
		a[p].l[0]=min(a[p].l[0],a[rs[p]].l[0]);
		a[p].l[1]=min(a[p].l[1],a[rs[p]].l[1]);
		a[p].r[0]=max(a[p].r[0],a[rs[p]].r[0]);
		a[p].r[1]=max(a[p].r[1],a[rs[p]].r[1]);
	}
}
inline void bld(int &p,int l,int r) {
	if(l>r) {
		p=0; return;
	}
	int mid=l+r>>1;
	double a0=0,a1=0,b0=0,b1=0;
	for(int i=l;i<=r;i++) {
		a0+=a[c[i]].x,b0+=a[c[i]].y;
	}
	a0/=(r-l+1),b0/=r-l+1;
	for(int i=l;i<=r;i++) {
		a1+=(a[c[i]].x-a0)*(a[c[i]].x-a0);
		b1+=(a[c[i]].y-b0)*(a[c[i]].y-b0);
	}
	if(a1>b1) {
		nth_element(c+l,c+mid,c+r+1,cmpx);
		p=c[mid]; a[p].w=0;	
	} else {
		nth_element(c+l,c+mid,c+r+1,cmpy);
		p=c[mid]; a[p].w=1;	
	}
	bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
	a[p].c=1,a[p].t=0;
	up(p);
}

int tot;
void dfs(int u) {
	a[u].x=++tot,sz[u]=1; c[u]=u;
	for(auto v:V[u]) {
		a[v].y=a[u].y+1;
		dfs(v);
		sz[u]+=sz[v];
	}                                                                           
}

inline void down(int p){
	if(a[p].t) {
		if(ls[p]) {
			a[ls[p]].c=a[ls[p]].t=a[p].t;
		}
		if(rs[p]) {
			a[rs[p]].c=a[rs[p]].t=a[p].t;
		}
		a[p].t=0;
	}
}
void upd(int p,int x1,int Y1,int x2,int y2,int k) {
	if(a[p].l[0]>x2||a[p].r[0]<x1||Y1>a[p].r[1]||y2<a[p].l[1]) {
		return;
	}
	if(x1<=a[p].l[0]&&a[p].r[0]<=x2&&Y1<=a[p].l[1]&&a[p].r[1]<=y2) {
		a[p].c=k,a[p].t=k; return;
	}
	down(p);
	if(x1<=a[p].x&&a[p].x<=x2&&Y1<=a[p].y&&a[p].y<=y2) {
		a[p].c=k; 
	}
	upd(ls[p],x1,Y1,x2,y2,k),upd(rs[p],x1,Y1,x2,y2,k);
}

int ask(int p,int x) {
	if(p==x) {
		return a[p].c;
	}
	down(p);
	if(a[p].w==0) {
		if(a[p].x>a[x].x) return ask(ls[p],x);
		return ask(rs[p],x);
	} else {
		if(a[p].y>a[x].y||a[p].y==a[x].y&&a[p].x>a[x].x) return ask(ls[p],x);
		return ask(rs[p],x); 
	}
}
int main(){
int T=rd();
while(T--) {
	n=rd(),rd(),m=rd();
	for(int i=1;i<=n;i++) {
		V[i].clear();
		vector<int>().swap(V[i]);
	}
	for(int i=2;i<=n;i++) {
		V[rd()].push_back(i);
	}
	tot=0; dfs(1);
	bld(rt,1,n);
	int ans=0;
	for(int i=1;i<=m;i++) {
		int u=rd(),l=rd(),k=rd();
		if(k!=0) {
			upd(rt,a[u].x,a[u].y,a[u].x+sz[u]-1,a[u].y+l,k);
		} else {
			ans=((ll)i*ask(rt,u)+ans)%P;
		}
	}
	printf("%d\n",ans);
}
return 0;
}
posted @ 2021-03-17 16:32  wwwsfff  阅读(61)  评论(0)    收藏  举报