[2018.10.23 T3] 新的家园

6 篇文章 0 订阅
4 篇文章 0 订阅

暂无链接

新的家园

——过去已成为过去,而未来终将是过去。所有事物在出现的那一刻都注定成为历史……

题目背景

公元 8102 8102 8102 年,火星的生态环境遭到严重破坏,终于也难逃人类的掠夺,变成了一颗死星。

所有人,都乘坐飞船逃往了无尽的深空,除了你。

默默地,你步行在荒芜的街道上,凝视着这片空无一人的大地——这个你由你亲自见证创造,又亲眼目送它毁灭的曾经人类的第二家园。

沙暴,四处是暗红的赤铁尘土卷起的沙暴。

红色的烟尘之中,你望见了远处废弃的行星通道,往事如潮水般涌入脑海……

题目描述

你还记得行星通道系统有 n n n个站点, E E E条道路。这里的道路同时包括了行星赤道上的行星通道和地面上的道路。每一条道路都是双向通行的,且从任意一端通过第 i i i条道路需要的时间都为 w i w_i wi。因为站点分布在赤道上,所以一定有一些边从 1 1 1号站点开始依次连接 2 , 3 , 4 , ⋯ 2,3,4,\cdots 2,3,4,号站点,并最终从 n n n号站点连回 1 1 1号点。

最后一班飞船即将起航,所剩的时间不多了。你决定沿着行星通道上的站点移动,最后一次认真看看这个深红的星球。

为了规划路线,你需要知道一些站点之间移动耗费的最短时间。具体的来说,有 q q q个询问。每一个询问 s i , t i s_i,t_i si,ti表示询问从 s i s_i si t i t_i ti的最短用时。 (注:上一题的条件在本题中不一定适用)

【输入】

第一行三个整数 n , E , q n,E,q n,E,q,表示站点数,道路数量和询问数。

接下来 E E E行每行三个整数 u , v , w u,v,w u,v,w表示 u , v u,v u,v间有一条双向道路,通行时间为 w w w

接下来 q q q行每行两个整数 s , t s,t s,t,表示询问 s s s站点到 t t t站点的最短用时。

【输出】

一共有 q q q行输出。

每一行一个整数,表示每一次询问的答案。

【输入样例】

5 6 3
1 2 1
2 5 1
2 3 1
3 4 1
4 5 1
5 1 1
2 5
1 5
3 5

【输出样例】

1
1
2

【提示】
【数据范围】

m = E − n m=E−n m=En
3.png
*:保证所有环以外的边以 ( a i , b i ) (ai,bi) (ai,bi)形式依次给出,对于 1 ≤ i &lt; n 1≤i&lt;n 1i<n,保证 a i &lt; b i a_i&lt;b_i ai<bi b i &lt; a i + 1 b_i&lt;a_i+1 bi<ai+1

保证 0 ≤ w ≤ 1000 0≤w≤1000 0w1000,答案在 2 31 − 1 2^{31}−1 2311内。

题解

发现存在决策的节点只有通道的两端,大环上的节点许多是没有决策的,对于这些没有决策的节点,我们可以把这些点变成一条边,整张图只保留是通道端点的点,这样整张图大小就是 O ( m ) O(m) O(m)的了。

询问时,把起点终点加入图中,跑一次最短路,得到答案后再把新加的边删掉,总复杂度为 O ( q × 最 短 路 ( m , m ) ) O(q\times 最短路(m,m)) O(q×(m,m))

代码
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int M=5e5+5;
struct sd{int to,w;}ed[M<<1];
bool operator<(sd a,sd b){return a.w>b.w;}
int loop[M],head[M],nxt[M<<1],dis[M],gra[M],le[M],ri[M],tot,n,m,q,cnt;
bool key[M],vis[M];
priority_queue<sd>dui;
void adde(int f,int t,int w){nxt[++cnt]=head[f],head[f]=cnt,ed[cnt]=(sd){t,w};}
void add(int u,int v,int w){adde(u,v,w),adde(v,u,w);}
void in()
{
	memset(loop,127,sizeof(loop));
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1,a,b,c;i<=m;++i)
	{
		scanf("%d%d%d",&a,&b,&c);
		if(a>b)swap(a,b);
		a==b-1?loop[a]=min(loop[a],c):(add(a,b,c),key[a]=key[b]=1);
	}
}
int dijkstra(int s,int t)
{
	for(int i=1;i<=tot;++i)dis[gra[i]]=inf,vis[gra[i]]=0;
	dui.push((sd){s,dis[s]=0});
	for(sd f;!dui.empty();)
	{
		f=dui.top(),dui.pop();
		if(vis[f.to])continue;
		vis[f.to]=1;
		for(int i=head[f.to];i;i=nxt[i])
		{
			if(!vis[ed[i].to]&&dis[ed[i].to]>dis[f.to]+ed[i].w)
			dis[ed[i].to]=dis[f.to]+ed[i].w,dui.push((sd){ed[i].to,dis[ed[i].to]});
		}
	}
	return dis[t];
}
void ac()
{
	for(int i=1,last=0;i<=n;++i)
	{
		loop[i]+=loop[i-1],le[i]=last;
		if(key[i]&&last)add(last,i,loop[i-1]-loop[last-1]);
		if(key[i])last=i,gra[++tot]=i;
	}
	for(int i=n,last=0;i;--i){ri[i]=last;if(key[i])last=i;}
	for(int i=1,s,t,x1,x2,x3,x4,x5,x6,pre=cnt;i<=q;++i)
	{
		scanf("%d%d",&s,&t);if(s>t)swap(s,t);
		if(s==t){puts("0");continue;}
		x1=head[le[s]],x2=head[s],x3=head[le[t]],x4=head[t],x5=head[ri[s]],x6=head[ri[t]];
		add(le[s],s,loop[s-1]-loop[le[s]-1]),add(s,ri[s],loop[ri[s]-1]-loop[s-1]);
		add(le[t],t,loop[t-1]-loop[le[t]-1]),add(t,ri[t],loop[ri[t]-1]-loop[t-1]);
		add(s,t,loop[t-1]-loop[s-1]);
		dis[s]=dis[t]=inf,vis[s]=vis[t]=0;
		printf("%d\n",dijkstra(s,t));
		cnt=pre,head[le[s]]=x1,head[s]=x2,head[le[t]]=x3,head[t]=x4,head[ri[s]]=x5,head[ri[t]]=x6;
	}
}
int main(){in();ac();}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值