suxxsfe

一言(ヒトコト)

P3831 [SHOI2012]回家的路

P3831 [SHOI2012]回家的路

分层图基础题,就是建图稍有麻烦


 

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
//P3831 [SHOI2012]回家的路 https://www.luogu.com.cn/problem/P3831
//一个网格,只能沿一个方向(横或纵)走
//某些交叉点可以改变方向
//沿一个方向走的时候,走一格花费两个时间,改变一次方向花费一个时间 
//给出这些点,问网格中两点之间最短时间

//分层图,为每个可以改变方向的点(按照题目背景是换乘点),分别创建两个虚拟节点
//一个是横着走的,也就是走到这里只能继续横着走
//一个是纵列方向走
//然后,对于每一横行,依次从左往右把这些在一行的点连边
//纵列也是如此 
//然后每一个换乘点,横纵方向的两个点之间连一个时间为 1 的点,表示换成

//起点和终点也看作换乘点,这样方便我们连边
//只不过它们的横纵方向的两点间边权为 0 ,因为在起点并不存在换乘的问题 
 
//1-m 是横列换乘点的编号
//m+1 是起点的横列 
//m+2 是终点的横列 
//m+3-2*m+2 是纵列换乘点的编号
//2*m+3 起点纵列
//2*m+4 终点纵列
//1-m 分别和 m+2-2*m+2 连权值为 1 的边
//m+1 和 2*m+3 以及 m+2 和 2*m+4 连权值为 0 的边 
//以 m+1 或 2*m+3 任意一个为虚构的图中的起点 
//以 m+2 或 2*m+4 任意一个为虚构的图中的终点 
//设横列编号为 id 则对应的纵列编号为 id+m+2

//数组尽量开大,一开始算错 WA 了 
struct data{
	int x,y,id;
}a[100009];
int dis[200009],in[200009];
int fir[200009],nex[700006],to[700006],w[700006],tot;
int heap[200009],size; 
inline int cmpx(data aa,data aaa){return aa.x==aaa.x?aa.y<aaa.y:aa.x<aaa.x;}
inline int cmpy(data aa,data aaa){return aa.y==aaa.y?aa.x<aaa.x:aa.y<aaa.y;}
inline void add(int u,int v,int len){
	to[++tot]=v;w[tot]=len;
	nex[tot]=fir[u];fir[u]=tot;
}
inline void link(int u,int v,int len){
	add(u,v,len);add(v,u,len);
}
inline void push(int x){
	heap[++size]=x;
	reg int i=size,fa;
	while(i>1){
		fa=i>>1;
		if(dis[heap[fa]]<=dis[heap[i]]) return;
		std::swap(heap[fa],heap[i]);i=fa;
	}
}
inline int pop(){
	int ret=heap[1];heap[1]=heap[size--];
	int i=1,ls,rs;
	while((i<<1)<=size){
		ls=i<<1;rs=ls|1;
		if(rs<=size&&dis[heap[rs]]<dis[heap[ls]]) ls=rs;
		if(dis[heap[ls]]>=dis[heap[i]]) break;
		std::swap(heap[i],heap[ls]);i=ls;
	}
	return ret;
}
inline void dij(int start){
	std::memset(dis,0x3f,sizeof dis);dis[start]=0;
	push(start);in[start]=1;
	while(size){
		reg int u=pop(),v;in[u]=0;
		for(reg int i=fir[u];i;i=nex[i]){
			v=to[i];
			if(dis[v]>dis[u]+w[i]){
				dis[v]=dis[u]+w[i];
				if(!in[v]) push(v),in[v]=1;
			}
		}
	}
}
inline void build(int m){
	std::sort(a+1,a+1+m+2,cmpx);
	for(reg int i=1;i<=m+2;){
		int now_x=a[i].x;
		for(i++;i<=m+2&&a[i].x==now_x;i++) link(a[i].id,a[i-1].id,(a[i].y-a[i-1].y)<<1);
	}
	std::sort(a+1,a+1+m+2,cmpy);
	for(reg int i=1;i<=m+2;){
		int now_y=a[i].y;
		for(i++;i<=m+2&&a[i].y==now_y;i++) link(a[i].id+m+2,a[i-1].id+m+2,(a[i].x-a[i-1].x)<<1);
	}
	for(reg int i=1;i<=m;i++) link(i,i+m+2,1);
	link(m+1,2*m+3,0);link(m+2,2*m+4,0);
}
int main(){
	read();int m=read();
	for(reg int i=1;i<=m+2;i++) a[i].x=read(),a[i].y=read(),a[i].id=i;
	build(m);
	dij(m+1);
	std::printf("%d",dis[2*m+4]==0x3f3f3f3f?-1:dis[2*m+4]);
//		EN;EN;
//		for(reg int i=1;i<=m*2+4;i++){
//			std::printf("%d :  ",i);
//			for(reg int j=fir[i];j;j=nex[j]) std::printf("%d ",to[j]);
//			EN;
//		}
	return 0;
}
posted @ 2020-04-13 14:45  suxxsfe  阅读(162)  评论(0编辑  收藏  举报