[NOI2019][洛谷P5471]弹跳(dijkstra+KD-Tree)

题面

https://www.luogu.com.cn/problem/P5471

题解

前置知识:

如果我们对于每一个弹跳装置i,从\(p_i\)\(x \in [l_i,r_i],y \in [d_i,u_i]\)的所有点都连一条权值为\(t_i\)的边,再跑一遍dijkstra,这样的答案一定是正确的,可是这样除了空间不够外,还大大地超时了。

所以考虑使用KD-Tree来优化这一过程。dijkstra的步骤是,每次找出未访问点中,当前dis最小的点u,将u标记为已访问,再用u出发的边去更新其他的点。

可以将所有的点建出一棵KD-Tree,并在KD-Tree上维护全局未访问点的最小dis值以及取到该点的点u。用KD-Tree模拟dijkstra的过程,每次先取出维护的u和dis,然后对于u出发的每一个弹跳装置,相当于是一个矩形范围内的所有点,它们的dis值要和\(dis[u]+t_i\)取较小值。一般情况下这是无法做到的,但是由于本题只需要维护dis的最小值的特殊性,此时可以做到。

总时间复杂度\(O(n \sqrt{n})\)

代码

#include<bits/stdc++.h>

using namespace std;

#define rg register
#define In inline

const int N = 7e4;
const int M = 15e4;
const int inf = 0x3f3f3f3f;

namespace IO{
	In int read(){
		int s = 0,ww = 1;		
		char ch = getchar();
		while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
		while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
		return s * ww;
	}
	In void write(int x){
		if(x < 0)putchar('-'),x = -x;
		if(x > 9)write(x / 10);
		putchar('0' + x % 10);
	}
}
using namespace IO;

In void chkmax(int &x,int y){if(x < y)x = y;}
In void chkmin(int &x,int y){if(x > y)x = y;}

struct point{
	int c[2];
}p[N+5];

int Dim;
int per[N+5];

In bool cmp(point a,point b){
 	return a.c[Dim] != b.c[Dim] ? a.c[Dim] < b.c[Dim] : a.c[Dim^1] < b.c[Dim^1]; 
}

In bool cmp1(int a,int b){
	return cmp(p[a],p[b]);
}

In bool include(int L,int R,int l,int r){
	return L <= l && r <= R;
}

In bool include(int L,int R,int D,int U,int l,int r,int d,int u){
	return include(L,R,l,r) && include(D,U,d,u);
}

In bool its(int L,int R,int l,int r){
	return !(R < l || r < L);
}

In bool its(int L,int R,int D,int U,int l,int r,int d,int u){
	return its(L,R,l,r) && its(D,U,d,u);
}

bool vis[N+5];

struct KDTree{
	int cnt,s[N+5][2],m[N+5][2],lc[N+5],rc[N+5];
	int dis[N+5],minn[N+5],ans[N+5],flag[N+5]; //当前节点对应的dis/当前子树中未访问点中最小的dis/取到当前子树最小dis的点/min标记	
	int idx[N+5]; //当前kdtree节点对应原来的第几个点
	void pushdown(int u){
		if(flag[u] == inf)return;
		int l = lc[u],r = rc[u];
		if(l){
			chkmin(dis[l],flag[u]);
			chkmin(flag[l],flag[u]);
			chkmin(minn[l],flag[u]);
		}
		if(r){
			chkmin(dis[r],flag[u]);
			chkmin(flag[r],flag[u]);
			chkmin(minn[r],flag[u]);
		}
		flag[u] = inf;
	}
	void pushup(int u){ 
		minn[u] = vis[idx[u]] ? inf + 1 : dis[u],ans[u] = u;
		if(lc[u] && !vis[idx[ans[lc[u]]]])if(minn[lc[u]] < minn[u])minn[u] = minn[lc[u]],ans[u] = ans[lc[u]];
		if(rc[u] && !vis[idx[ans[rc[u]]]])if(minn[rc[u]] < minn[u])minn[u] = minn[rc[u]],ans[u] = ans[rc[u]];	
	}
	int build(int l,int r,int dim){
		if(l > r)return 0;
		int u = ++cnt;
		int mid = (l + r) >> 1;
		Dim = dim;
		nth_element(per + l,per + mid,per + r + 1,cmp1);
		idx[u] = per[mid];
		dis[u] = per[mid] == 1 ? 0 : inf;
		flag[u] = inf;
		s[u][0] = m[u][0] = p[per[mid]].c[0];
		s[u][1] = m[u][1] = p[per[mid]].c[1];
		lc[u] = build(l,mid - 1,dim ^ 1);
		rc[u] = build(mid + 1,r,dim ^ 1);
		int L = lc[u],R = rc[u];
		if(L){
			chkmax(s[u][0],s[L][0]);chkmin(m[u][0],m[L][0]);
			chkmax(s[u][1],s[L][1]);chkmin(m[u][1],m[L][1]);
		}
		if(R){
			chkmax(s[u][0],s[R][0]);chkmin(m[u][0],m[R][0]);
			chkmax(s[u][1],s[R][1]);chkmin(m[u][1],m[R][1]);
		}
		pushup(u);
		return u;
	}
	void ud1(int u,int i,int dim){ //标记已使用
		if(idx[u] == i){
			pushdown(u);
			pushup(u);
			return;
		}
		pushdown(u);
		Dim = dim;
		if(cmp(p[i],p[idx[u]]))ud1(lc[u],i,dim ^ 1);
		else ud1(rc[u],i,dim ^ 1);
		pushup(u);
	}
	void ud2(int u,int ql,int qr,int qd,int qu,int x){ //区域与目标值取min
		if(flag[u] <= x)return;
		if(include(ql,qr,qd,qu,m[u][0],s[u][0],m[u][1],s[u][1])){
			chkmin(minn[u],x);
			chkmin(flag[u],x);
			chkmin(dis[u],x);
			return;
		}
		if(include(ql,qr,qd,qu,p[idx[u]].c[0],p[idx[u]].c[0],p[idx[u]].c[1],p[idx[u]].c[1]))
			chkmin(dis[u],x);
		pushdown(u);
		int L = lc[u],R = rc[u];
		if(L && its(m[L][0],s[L][0],m[L][1],s[L][1],ql,qr,qd,qu))ud2(L,ql,qr,qd,qu,x);
		if(R && its(m[R][0],s[R][0],m[R][1],s[R][1],ql,qr,qd,qu))ud2(R,ql,qr,qd,qu,x);
		pushup(u);
	}
	In void query(int &u,int &d){
		u = idx[ans[1]],d = minn[1];
	}
}T;

struct equip{
	int t,l,r,d,u;
}e[M+5];

vector<int>v[N+5];
int ans[N+5];
int n,m,w,h;

void dij(){
	memset(ans,0x3f,sizeof(ans));
	ans[1] = 0;
	for(rg int i = 1;i <= n;i++){
		int u,d;
		T.query(u,d);
		ans[u] = d;
		for(rg int j = 0;j < v[u].size();j++){
			int id = v[u][j];
			T.ud2(1,e[id].l,e[id].r,e[id].d,e[id].u,d + e[id].t);
		}
		vis[u] = 1;
		T.ud1(1,u,0); 
	}
}

int main(){
	n = read(),m = read(),w = read(),h = read();
	for(rg int i = 1;i <= n;i++)p[i].c[0] = read(),p[i].c[1] = read();
	for(rg int i = 1;i <= n;i++)per[i] = i;
	T.build(1,n,0);
	for(rg int i = 1;i <= m;i++){
		int p = read();
		v[p].push_back(i);
		e[i].t = read(),e[i].l = read(),e[i].r = read(),e[i].d = read(),e[i].u = read();
	}
	dij();
	for(rg int i = 2;i <= n;i++)write(ans[i]),putchar('\n');
	return 0;
}
posted @ 2020-10-05 23:18  coder66  阅读(227)  评论(0编辑  收藏  举报