• [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('
    ');
    	return 0;
    }
    
  • 相关阅读:
    Java(14):面向对象、封装、继承、方法重写、多态、抽象类与接口、内部类
    Java(13):数组、Arrays类、冒泡排序
    Java(12):方法、重载、命令行传参、可变参数、方法调用
    Java(11):switch、dowhile、九九乘法表、打印质数、打印三角形
    Java(10):用户交互Scanner
    Java(9):包
    Java(8):运算符
    Java(7):变量和常量及其规范、作用域
    Mybatis 打印日志
    mysql 更新数据
  • 原文地址:https://www.cnblogs.com/xh092113/p/12520908.html
Copyright © 2020-2023  润新知