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


    点此看题面

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

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

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

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

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

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

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

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

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

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

    代码:(O(msqrt 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('
    ');}
    }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后跑最短路
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    数据仓库
    数据库事务隔离级别与锁
    并发包之Future:代码级控制超时时间
    axis2 webservice 发布、调用与项目集成
    配置远程控制
    解决局部刷新的问题
    Sharepoint 2013 搜索高级配置(Search Scope)
    重启IIS报错:IIS 服务或万维网发布服务,或者依赖这 服务可能在启动期间发生错误或者已禁用
    错误提示:此产品的试用期已经结束
    Sharepoint 2013 启用搜做服务
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5471.html
Copyright © 2020-2023  润新知