把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5471】[NOI2019] 弹跳(KD-Tree+最短路)

点此看题面

  • 一张\(w\times h\)的二维平面内有\(n\)个城市。有\(m\)个发射器,第\(i\)个发射器在城市\(p_i\)中,在\(p_i\)通过这个发射器能花费\(t_i\)的时间到达二维区间\([L_i\sim R_i][D_i\sim U_i]\)内的任意城市。
  • 求从\(1\)号城市出发,到每个城市的最短时间。
  • \(n\le7\times10^4,m\le1.5\times10^5\)

\(KD-Tree\)优化建图(伪)

对于这种二维区间的问题,除了二维线段树以外,首先想到的就应该是\(KD-Tree\)了。

而这道题要求的显然就是从\(1\)号点出发的单源最短路,因此只要\(KD-Tree\)优化建图即可。

当然,这样建图是会炸空间的。

实际上,对于这类数据结构优化最短路的问题,我们并不用真的把图建出来。

只要用\(KD-Tree\)维护好子树内所有点\(dis\)的最小值及对应节点以便每次\(Dijkstra\)找距离最小的点,并开一个子树取\(min\)的标记来处理二维区间内距离的修改即可。

当然常规的什么每维坐标的范围肯定也是要维护的不用说。

还有就是每个点作为距离最小的点扩展过一次后就不能再扩展了,这只要打个标记在\(PushUp\)的时候不考虑其贡献即可。

口胡起来就这么简单,具体实现就仁者见仁了。

代码:\(O(m\sqrt n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 70000
#define M 150000
#define LL long long
#define INF (int)1e9
#define Pr pair<LL,int>
#define mp make_pair
#define add(x,i,l,r,d,u) (e[++ee]=(edge){i,l,r,d,u,lnk[x]},lnk[x]=ee)
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m,w,h,ee,lnk[N+5];struct edge {int t,L,R,D,U,nxt;}e[M+5];
int D;struct P
{
	int p,x[2];I P(CI a=0,CI b=0) {x[0]=a,x[1]=b;}
	I int& operator [] (CI d) {return x[d];}
	I bool operator < (Con P& o) Con {return x[D]<o.x[D];}
}p[N+5],p_[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int ti;
class KDTree
{
	private:
		#define PD(x) (O[x].Z<1e18&&(T(O[x].S[0],O[x].Z),T(O[x].S[1],O[x].Z)))
		#define T(x,v) ((x)&&(Gmin(O[x].V,v),Gmin(O[x].Z,v),O[x].G.second&&Gmin(O[x].G.first,v)))
		struct node {P Mn,Mx;Pr G;LL V,Z;int fg,F,S[2];I void Init(Con P& p) {Mn=Mx=p,G=mp(1e18,p.p),V=Z=1e18;}}O[N+5];
		I void PU(CI x)//上传信息
		{
			RI lc=O[x].S[0],rc=O[x].S[1];
			O[x].Mn=P(min(p[x][0],min(O[lc].Mn[0],O[rc].Mn[0])),min(p[x][1],min(O[lc].Mn[1],O[rc].Mn[1]))),//两维最小值
			O[x].Mx=P(max(p[x][0],max(O[lc].Mx[0],O[rc].Mx[0])),max(p[x][1],max(O[lc].Mx[1],O[rc].Mx[1]))),//两维最大值
			O[x].G=min(mp(O[x].fg?(LL)2e18:O[x].V,x),min(O[lc].G,O[rc].G)),O[x].G.second&&Gmin(O[x].G.first,O[x].Z);//fg记录已使用过
		}
	public:
		int rt;I KDTree() {O[0].Mn=P(INF,INF),O[0].Mx=P(0,0),O[0].G=mp(2e18,0),O[0].V=O[0].Z=2e18;}//初始化,把0的信息设成INF
		I int Build(P* p,CI l,CI r,CI d=0)//初始建树
		{
			RI rt,mid=l+r>>1;D=d,nth_element(p+l,p+mid,p+r+1),O[rt=p[mid].p].Init(p[mid]);//取出中间点为根
			l^mid&&(O[O[rt].S[0]=Build(p,l,mid-1,d^1)].F=rt),r^mid&&(O[O[rt].S[1]=Build(p,mid+1,r,d^1)].F=rt);//分别建左右子树
			return PU(rt),rt;//上传信息后返回当前点编号
		}
		I void U(CI rt,Con LL& v,CI l,CI r,CI d,CI u)//一个二维区间向v取较小值
		{
			if(!rt||O[rt].Z<v||O[rt].Mx[0]<l||O[rt].Mn[0]>r||O[rt].Mx[1]<d||O[rt].Mn[1]>u) return;//如果修改无意义或完全不在区间内
			if(l<=O[rt].Mn[0]&&O[rt].Mx[0]<=r&&d<=O[rt].Mn[1]&&O[rt].Mx[1]<=u) return (void)T(rt,v);//如果完全在区间内
			PD(rt),l<=p[rt][0]&&p[rt][0]<=r&&d<=p[rt][1]&&p[rt][1]<=u&&Gmin(O[rt].V,v);//如果当前点在区间内
			U(O[rt].S[0],v,l,r,d,u),U(O[rt].S[1],v,l,r,d,u),PU(rt);//递归修改
		}
		int St[N+5];I void E(RI x) {RI _x=x,T=0;W(St[++T]=_x,_x=O[_x].F);//标记一个点已使用
			RI i;for(i=T;i;--i) PD(St[i]);for(O[x].fg=i=1;i<=T;++i) PU(St[i]);}//先下推所有标记,然后上传信息
		I Pr Q() {return O[rt].G;}//询问最小值及对应编号
}K;
LL dis[N+5];I void Dij()//KD-Tree优化Dijkstra
{
	Pr k;for(RI t=1,i;t<=n;++t)//总共会取出n次点
	{
		t^1?(k=K.Q(),K.E(k.second),dis[k.second]=k.first):(k=mp(0,1),0);//第一次从1出发,否则每次从KD-Tree中寻找
		for(i=lnk[k.second];i;i=e[i].nxt) K.U(K.rt,k.first+e[i].t,e[i].L,e[i].R,e[i].D,e[i].U);//每条边在KD-Tree上修改
	}
	for(RI i=2;i<=n;++i) writeln(dis[i]);//输出距离
}
int main()
{
	RI i;for(read(n,m,w,h),i=1;i<=n;++i) read(p[i].x[0],p[i].x[1]),p[i].p=i,p_[i]=p[i];//p_复制一遍p,建KD-Tree用
	RI x,t,l,r,d,u;for(i=1;i<=m;++i) read(x,t,l,r,d,u),add(x,t,l,r,d,u);
	return K.rt=K.Build(p_,2,n),Dij(),clear(),0;//建出KD-Tree后跑最短路
}
posted @ 2021-03-25 13:44  TheLostWeak  阅读(53)  评论(0编辑  收藏  举报