ybtAu「高级数据结构」第2章 K-D树

KDT 的替罪羊树重构复杂度是假的。

A. 【例题1】SJY摆棋子

单点插入,查询最小曼哈顿距离。
如果查询点到节点矩形距离大于答案,剪枝。
由于有单点插入,所以需要根号重构。

#include <iostream>
#include <algorithm>
#define N 1000005
int n,m,D,rt,ans;
struct Node
{
	int x[2];
	bool operator <(const Node &g) const {return (x[D]!=g.x[D])?(x[D]<g.x[D]):(x[D^1]<g.x[D^1]);}
} a[N];
int md(Node x,Node y)
{
	int r=0;
	for(int i=0;i<2;i++) r+=abs(x.x[i]-y.x[i]);
	return r;
}
namespace KDT
{
	int ls[N],rs[N],idx;
	Node d[N],lb[N],rb[N];
	void pu(int x)
	{
		for(int i=0;i<2;i++)
		{
			if(ls[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[ls[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[ls[x]].x[i]);
			if(rs[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[rs[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[rs[x]].x[i]);
		}
	}
	int build(int l,int r,int di)
	{
		if(l>r) return 0;
		D=di;
		int mid=l+r>>1,x=++idx;
		std::nth_element(a+l,a+mid,a+r+1);
		d[x]=lb[x]=rb[x]=a[mid];
		ls[x]=build(l,mid-1,di^1),rs[x]=build(mid+1,r,di^1);
		pu(x);
		return x;
	}
	void ins(int &x,int t,int di)
	{
		if(!x) return (void)(x=++idx,d[x]=lb[x]=rb[x]=a[t]);
		D=di;
		if(a[t]<d[x]) ins(ls[x],t,di^1);
		else ins(rs[x],t,di^1);
		pu(x);
	}
	int edis(int x,Node t)
	{
		if(!x) return 1e9;
		int r=0;
		for(int i=0;i<2;i++)
		{
			if(t.x[i]<lb[x].x[i]) r+=lb[x].x[i]-t.x[i];
			if(t.x[i]>rb[x].x[i]) r+=t.x[i]-rb[x].x[i];
		}
		return r;
	}
	void qr(int x,Node t,int di)
	{
		if(!x) return;
		ans=std::min(ans,md(t,d[x]));
		int el=edis(ls[x],t),er=edis(rs[x],t);
		if(el<er)
		{
			if(el<ans) qr(ls[x],t,di^1);
			if(er<ans) qr(rs[x],t,di^1);
		}
		else
		{
			if(er<ans) qr(rs[x],t,di^1);
			if(el<ans) qr(ls[x],t,di^1);
		}
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::cin>>n>>m;
	for(int i=1,x,y;i<=n;i++) std::cin>>x>>y,a[i]=(Node){x,y};
	rt=KDT::build(1,n,0);
	int idx=n,cnt=0;
	for(int i=1,t,x,y;i<=m;i++)
	{
		std::cin>>t>>x>>y;
		if(t==1)
		{
			a[++idx].x[0]=x,a[idx].x[1]=y;
			KDT::ins(rt,idx,0);
			cnt++;
			if(cnt>=10000) KDT::idx=0,rt=KDT::build(1,idx,0),cnt=0;
		}
		if(t==2)
		{
			ans=1e9;
			KDT::qr(rt,(Node){x,y},0);
			std::cout<<ans<<'\n';
		}
	}
}

B. 【例题2】查找k远点

维护一个大小为 \(k\) 的小根堆,如果查询点到节点矩形大于堆顶,剪枝。

#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>
#define N 100005
#define int long long
int n,m,K,D,rt;
struct cmp
{
	int x;
	double y;
	cmp(int tx=0,double ty=0):x(tx),y(ty) {}
	bool operator <(const cmp &g) const
	{
		return y>g.y||(y==g.y&&x<g.x);
	}
};
std::priority_queue<cmp> pq;

void initpq()
{
	while(!pq.empty()) pq.pop();
	for(int i=0;i<K;i++) pq.push((cmp){0,-1e30});
}
struct Node
{
	int x[2],id;
	Node(int p1=0,int p2=0,int pid=0):id(pid) {x[0]=p1,x[1]=p2;}
	bool operator <(const Node &g) const {return (x[D]!=g.x[D])?(x[D]<g.x[D]):(x[D^1]<g.x[D^1]);}
} a[N];
double dis(Node x,Node y) {return hypotl(x.x[0]-y.x[0],x.x[1]-y.x[1]);}
namespace KDT
{
	Node d[N],lb[N],rb[N];
	int id[N],ls[N],rs[N],idx;
	void pu(int x)
	{
		for(int i=0;i<2;i++)
		{
			if(ls[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[ls[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[ls[x]].x[i]);
			if(rs[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[rs[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[rs[x]].x[i]);
		}
	}
	int build(int l,int r,int di)
	{
		if(l>r) return 0;
		int mid=l+r>>1,x=++idx;
		D=di;
		std::nth_element(a+l,a+mid,a+r+1);
		d[x]=lb[x]=rb[x]=a[mid],id[x]=a[mid].id;
		ls[x]=build(l,mid-1,di^1),rs[x]=build(mid+1,r,di^1);
		pu(x);
		return x;
	}
	double edis(int x,Node t)
	{
		if(!x) return -1e30;
		double r=0;
		for(int i=0;i<2;i++)
		{
			double t1=std::max(abs(t.x[i]-lb[x].x[i]),abs(t.x[i]-rb[x].x[i]));
			r+=t1*t1;
		}
		return sqrtl(r);
	}
	void qr(int x,Node t)
	{
		if(!x) return;
		cmp tmp={id[x],dis(d[x],t)};
		if(tmp<pq.top()) pq.pop(),pq.push(tmp);
		double el=edis(ls[x],t),er=edis(rs[x],t);
		if(el>=er)
		{
			qr(ls[x],t);
			if((cmp){id[rs[x]],er}<pq.top()) qr(rs[x],t);
		}
		else
		{
			qr(rs[x],t);
			if((cmp){id[ls[x]],el}<pq.top()) qr(ls[x],t);
		}
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::cin>>n;
	for(int i=1,x,y;i<=n;i++) std::cin>>x>>y,a[i]={x,y,i};
	rt=KDT::build(1,n,0);
	std::cin>>m;
	for(int i=1,x,y;i<=m;i++)
	{
		std::cin>>x>>y>>K;
		initpq();
		KDT::qr(rt,(Node){x,y});
		std::cout<<pq.top().x<<'\n';
	}
}

死因:重构时位于序列 \(mid\) 位置的节点编号不是 \(mid\)

C. 简单题

需要根号重构。
如果查询矩形与节点矩形无交,剪枝;如果查询矩形包含节点矩形,把当前节点和加入答案;如果当前节点自己在查询矩形内,加入答案。

#include <iostream>
#include <algorithm>
#define N 200005
int n,len,D,rt;
struct Node
{
	int x[2],v;
	Node(int p1=0,int p2=0,int v1=0):v(v1) {x[0]=p1,x[1]=p2;}
	bool operator <(const Node &g) const {return (x[D]!=g.x[D])?(x[D]<g.x[D]):(x[D^1]<g.x[D^1]);}
} a[N];
namespace KDT
{
	Node d[N],lb[N],rb[N];
	int sum[N],ls[N],rs[N],idx;
	bool checkin(Node l,Node r,Node mn,Node mx)
	{
		for(int i=0;i<2;i++) if(mn.x[i]>l.x[i]||mx.x[i]<r.x[i]) return 0;
		return 1;
	}
	bool checkout(int x,Node mn,Node mx)
	{
		for(int i=0;i<2;i++) if(mx.x[i]<lb[x].x[i]||mn.x[i]>rb[x].x[i]) return 1;
		return 0;
	}
	void pu(int x)
	{
		sum[x]=sum[ls[x]]+sum[rs[x]]+d[x].v;
		for(int i=0;i<2;i++)
		{
			if(ls[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[ls[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[ls[x]].x[i]);
			if(rs[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[rs[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[rs[x]].x[i]);
		}
	}
	int build(int l,int r,int di)
	{
		if(l>r) return 0;
		int mid=l+r>>1,x=++idx;
		D=di;
		std::nth_element(a+l,a+mid,a+r+1);
		d[x]=lb[x]=rb[x]=a[mid];
		ls[x]=build(l,mid-1,di^1),rs[x]=build(mid+1,r,di^1);
		pu(x);
		return x;
	}
	void ins(int &x,int t,int di)
	{
		if(!x) return (void)(x=++idx,ls[x]=rs[x]=0,d[x]=lb[x]=rb[x]=a[t],pu(x));
		D=di;
		if(a[t]<d[x]) ins(ls[x],t,di^1);
		else ins(rs[x],t,di^1);
		pu(x);
	}
	int qr(int x,Node mn,Node mx)
	{
		if(!x||checkout(x,mn,mx)) return 0;
		if(checkin(lb[x],rb[x],mn,mx)) return sum[x];
		int ret=0;
		if(checkin(d[x],d[x],mn,mx)) ret+=d[x].v;
		ret+=qr(ls[x],mn,mx)+qr(rs[x],mn,mx);
		return ret;
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::cin>>n;
	int la=0,tnc=0;
	for(int op,x1,y1,x2,y2,v;;)
	{
		std::cin>>op;
		if(op==3) break;
		if(op==1)
		{
			std::cin>>x1>>y1>>v;
			x1^=la,y1^=la,v^=la;
			a[++len]={x1,y1,v};
			KDT::ins(rt,len,0);
			tnc++;
			if(tnc>=10000) KDT::idx=0,rt=KDT::build(1,len,0),tnc=0;
		}
		if(op==2)
		{
			std::cin>>x1>>y1>>x2>>y2;
			x1^=la,y1^=la,x2^=la,y2^=la;
			std::cout<<(la=KDT::qr(rt,{x1,y1},{x2,y2}))<<'\n';
		}
	}
}

D. 捉迷藏

如果不想重构的话,需要查最大距离和次小距离,因为它本身不算。

#include <iostream>
#include <algorithm>
#define N 200005
int n,rt,D,mx;
struct Node
{
	int x[2];
	Node(int p0=0,int p1=0) {x[0]=p0,x[1]=p1;}
	bool operator <(const Node &g) const {return (x[D]!=g.x[D])?(x[D]<g.x[D]):(x[D^1]<g.x[D^1]);}
} a[N];
int dis(Node x,Node y) {return abs(x.x[0]-y.x[0])+abs(x.x[1]-y.x[1]);}
struct semi
{
	int v1,v2;
	semi():v1(1e9),v2(1e9) {}
	void upd(int x)
	{
		if(x<v1) v2=v1,v1=x;
		else if(x<v2) v2=x;
	}
} mn;
namespace KDT
{
	Node d[N],lb[N],rb[N];
	int ls[N],rs[N],idx;
	void pu(int x)
	{
		for(int i=0;i<2;i++)
		{
			if(ls[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[ls[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[ls[x]].x[i]);
			if(rs[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[rs[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[rs[x]].x[i]);
		}
	}
	int build(int l,int r,int di)
	{
		if(l>r) return 0;
		int mid=l+r>>1,x=++idx;
		D=di;
		std::nth_element(a+l,a+mid,a+r+1);
		d[x]=lb[x]=rb[x]=a[mid];
		ls[x]=build(l,mid-1,di^1),rs[x]=build(mid+1,r,di^1);
		pu(x);
		return x;
	}
	int emax(int x,Node t)
	{
		int r=0;
		for(int i=0;i<2;i++) r+=std::max(abs(t.x[i]-lb[x].x[i]),abs(t.x[i]-rb[x].x[i]));
		return r;
	}
	int emin(int x,Node t)
	{
		int r=0;
		for(int i=0;i<2;i++)
		{
			if(t.x[i]<lb[x].x[i]) r+=lb[x].x[i]-t.x[i];
			if(t.x[i]>rb[x].x[i]) r+=t.x[i]-rb[x].x[i];
		}
		return r;
	}
	void qmax(int x,Node t)
	{
		if(!x) return;
		mx=std::max(mx,dis(d[x],t));
		int el=emax(ls[x],t),er=emax(rs[x],t);
		if(el>er)
		{
			if(el>mx) qmax(ls[x],t);
			if(er>mx) qmax(rs[x],t);
		}
		else
		{
			if(er>mx) qmax(rs[x],t);
			if(el>mx) qmax(ls[x],t);
		}
	}
	void qmin(int x,Node t)
	{
		if(!x) return;
		mn.upd(dis(d[x],t));
		int el=emin(ls[x],t),er=emin(rs[x],t);
		if(el<er)
		{
			if(el<mn.v2) qmin(ls[x],t);
			if(er<mn.v2) qmin(rs[x],t);
		}
		else
		{
			if(er<mn.v2) qmin(rs[x],t);
			if(el<mn.v2) qmin(ls[x],t);
		}
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	std::cin>>n;
	for(int i=1,x,y;i<=n;i++) std::cin>>x>>y,a[i]={x,y};
	rt=KDT::build(1,n,0);
	int ans=1e9;
	for(int i=1;i<=n;i++)
	{
		mx=0,mn=semi();
		KDT::qmax(rt,a[i]),KDT::qmin(rt,a[i]);
		ans=std::min(ans,mx-mn.v2);
	}
	std::cout<<ans;
}

E. 染颜色

这题相比前几题没有那么裸,但是思维难度也不高。
有两条限制:距离不超过 \(l\),说明深度在 \([dep_a,dep_a+l]\) 内;是子节点,说明 \(dfn\in[dfn_a,dfn_a+siz_a-1]\)
考虑把 \(dep\) 看作 \(x\) 轴,\(dfn\) 看作 \(y\) 轴,于是变成了矩阵覆盖,单点查询问题。
矩阵覆盖需要打 tag。

#include <iostream>
#include <algorithm>
#define N 100005
#define int long long
#define mod 1000000007
int n,C,q,hed[N],tal[N],nxt[N],cnte,D,dfn[N],bot[N],dep[N],idix,rt;
void de(int u,int v) {tal[++cnte]=v,nxt[cnte]=hed[u],hed[u]=cnte;}
void dfs(int x)
{
	dfn[x]=bot[x]=++idix;
	for(int i=hed[x];i;i=nxt[i]) dep[tal[i]]=dep[x]+1,dfs(tal[i]),bot[x]=std::max(bot[x],bot[tal[i]]);
}
struct Node
{
	int x[2],v;
	Node(int p0=0,int p1=0,int v1=0):v(v1) {x[0]=p0,x[1]=p1;}
	bool operator <(const Node &g) const {return (x[D]!=g.x[D])?(x[D]<g.x[D]):(x[D^1]<g.x[D^1]);}
} a[N],b[N];
namespace KDT
{
	Node d[N],lb[N],rb[N];
	int tg[N],ls[N],rs[N],idx;
	void mt(int x,int y) {d[x].v=tg[x]=y;}
	void pd(int x) {if(tg[x]) mt(ls[x],tg[x]),mt(rs[x],tg[x]),tg[x]=0;}
	void pu(int x)
	{
		for(int i=0;i<2;i++)
		{
			if(ls[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[ls[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[ls[x]].x[i]);
			if(rs[x]) lb[x].x[i]=std::min(lb[x].x[i],lb[rs[x]].x[i]),rb[x].x[i]=std::max(rb[x].x[i],rb[rs[x]].x[i]);
		}
	}
	bool checkin(Node l,Node r,Node mn,Node mx)
	{
		for(int i=0;i<2;i++) if(l.x[i]<mn.x[i]||r.x[i]>mx.x[i]) return 0;
		return 1;
	}
	bool checkout(Node l,Node r,Node mn,Node mx)
	{
		for(int i=0;i<2;i++) if(l.x[i]>mx.x[i]||r.x[i]<mn.x[i]) return 1;
		return 0;
	}
	int build(int l,int r,int di)
	{
		if(l>r) return 0;
		int mid=l+r>>1,x=++idx;
		D=di;
		std::nth_element(a+l,a+mid,a+r+1);
		d[x]=lb[x]=rb[x]=a[mid],tg[x]=0;
		ls[x]=build(l,mid-1,di^1),rs[x]=build(mid+1,r,di^1);
		pu(x);
		return x;
	}
	void md(int x,Node mn,Node mx,int t)
	{
		if(!x||checkout(lb[x],rb[x],mn,mx)) return;
		if(checkin(lb[x],rb[x],mn,mx)) return (void)mt(x,t);
		pd(x);
		if(checkin(d[x],d[x],mn,mx)) d[x].v=t;
		md(ls[x],mn,mx,t),md(rs[x],mn,mx,t);
	}
	int qr(int x,int t)
	{
		if(!x||checkout(b[t],b[t],lb[x],rb[x])) return 0;
		if(b[t].x[0]==d[x].x[0]&&b[t].x[1]==d[x].x[1]) return d[x].v;
		pd(x);
		return qr(ls[x],t)|qr(rs[x],t);
	}
};
signed main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0),std::cout.tie(0);
	int T;
	std::cin>>T;
	while(T--)
	{
		for(int i=1;i<=n;i++) hed[i]=dfn[i]=dep[i]=0;
		idix=0;
		for(;cnte;cnte--) tal[cnte]=nxt[cnte]=0;
		std::cin>>n>>C>>q;
		for(int i=2,x;i<=n;i++) std::cin>>x,de(x,i);
		dfs(1);
		for(int i=1;i<=n;i++) b[i]=a[i]={dfn[i],dep[i],1};
		KDT::idx=0,rt=KDT::build(1,n,0);
		int ans=0;
		for(int i=1,x,y,z;i<=q;i++)
		{
			std::cin>>x>>y>>z;
			if(z) KDT::md(rt,{dfn[x],dep[x]},{bot[x],dep[x]+y},z);
			else (ans+=i*KDT::qr(rt,x)%mod)%=mod;
		}
		std::cout<<ans<<'\n';
	}
}
posted @ 2025-06-27 17:55  整齐的艾萨克  阅读(12)  评论(0)    收藏  举报