BZOJ3575: [Hnoi2014]道路堵塞

BZOJ3575: [Hnoi2014]道路堵塞

Description

A国有N座城市,依次标为1到N。同时,在这N座城市间有M条单向道路,每条道路的长度是一个正整数。现在,A国
交通部指定了一条从城市1到城市N的路径,并且保证这条路径的长度是所有从城市1到城市N的路径中最短的。不幸
的是,因为从城市1到城市N旅行的人越来越多,这条由交通部指定的路径经常发生堵塞。现在A国想知道,这条路
径中的任意一条道路无法通行时,由城市1到N的最短路径长度是多少。

Input

第一行是三个用空格分开的正整数N、M和L,分别表示城市数目、单向道路数目和交通部指定的最短路径包含多少条道路。
按下来M行,每行三个用空格分开的整数a、b和c,表示存在一条由城市a到城市b的长度为c的单向道路。
这M行的行号也是对应道路的编号,即其中第1行对应的道路编号为1,第2行对应的道路编号为2,…,第M行对应的道路编号为M。
最后一行为L个用空格分开的整数sp(1)…,,sp(L),依次表示从城市1到城市N的由交通部指定的最短路径上的道路的编号。
2<N<100000,1<M<200000。所用道路长度大于0小于10000。

Output

L行,每行为一个整数,第i行(i=1,2…,,L)的整数表示删去编号为sp(i)的道路后从城市1到城市N的最短路径长度。
如果去掉后没有从城市1到城市N的路径,则输出一1。

Sample Input

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

Sample Output

6
6

HINT

数据已加强By Vfleaking,另2017.9.1再加数据一组,未重测。


题解Here!
这个题自从2017.9.2的最后一个AC记录之后就没有人再AC了。。。
包括我也是。。。
这种无脑加强数据的题目基本上就是废题了。。。
当然,还可以到洛谷/LOJ上去搞搞事:

洛谷P3238 [HNOI2014]道路堵塞

LOJ#2209. 「HNOI2014」道路堵塞

$BZOJ$质量真心差。。。

正题:

首先我们不可能每次删掉当前边之后再跑一遍最短路。。。

那么我们必须想办法优化我的删边+求得最短路过程。

我们考虑删边之后,当前的从$1$到$n$的最短路只能是从$1$出发,在最短路径上走一段,再走一段非最短路,最后回到最短路径上。

这个应该能理解。

那么如果我们强制让$SPFA$不走当前边,在跑最短路的过程中,我只要发现走到了一个最短路径上的点上时,就可以用当前的距离更新一下$ans$。

我们可以用堆存走到哪个点导致了更新$ans$。

这样就可以在以后反复调用,保证全局最优了。

但是!每次跑$SPFA$无需清空$dis$,因为从左往右做的时候,$dis$显然递减!

想一想最短路的松弛操作就应该知道怎么回事了。

不过,到达最短路上的点的标记必须清空!

然后就可以跑了。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define MAXN 100010
#define MAX ((1<<30)-1)
using namespace std;
int n,m,q;
int top=0,stack[MAXN];
int num[MAXN],t[MAXN],pos[MAXN],f[MAXN],g[MAXN];
int head[MAXN],path[MAXN];
bool vis[MAXN],used[MAXN];
struct Graph{
	int next,to,w;
}a[MAXN<<1];
struct node{
	int x,dis;
	bool operator <(const node &p)const{
		return dis>p.dis;
	}
}b[MAXN];
priority_queue<node> heap;
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline int relax(int u,int v,int w){
	if(path[v]>path[u]+w){
		path[v]=path[u]+w;
		return 1;
	}
	return 0;
}
inline void add(int u,int v,int w,int c){
	a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
}
void spfa(int s,int w,int id,int time){
	int u,v;
	queue<int> que;
	for(int i=1;i<=q+1;i++)used[i]=false;
	path[s]=w;
	vis[s]=true;
	top=0;
	que.push(s);
	while(!que.empty()){
		u=que.front();
		que.pop();
		vis[u]=false;
		for(int i=head[u];i;i=a[i].next){
			if(i==id)continue;
			v=a[i].to;
			if(pos[v]>time){
				if(!used[pos[v]]){
					used[pos[v]]=true;
					stack[++top]=v;
					b[v].x=pos[v];
					b[v].dis=path[u]+a[i].w+g[pos[v]];
				}
				else b[v].dis=min(b[v].dis,path[u]+a[i].w+g[pos[v]]);
			}
			else{
				if(relax(u,v,a[i].w)&&!vis[v]){
					vis[v]=true;
					que.push(v);
				}
			}
		}
	}
	while(top)heap.push(b[stack[top--]]);
}
void work(){
	for(int i=1;i<=q;i++){
		spfa(t[i],f[i],num[i],i);
		while(!heap.empty()&&heap.top().x<=i){
			used[heap.top().x]=false;
			heap.pop();
		}
		if(heap.empty())printf("-1
");
		else printf("%d
",heap.top().dis);
	}
}
void init(){
	int u,v,w;
	n=read();m=read();q=read();
	for(int i=1;i<=n;i++){path[i]=MAX;vis[i]=false;}
	for(int i=1;i<=m;i++){
		u=read();v=read();w=read();
		add(u,v,w,i);
	}
	t[1]=pos[1]=1;
	for(int i=1;i<=q;i++){
		num[i]=read();
		t[i+1]=a[num[i]].to;
		pos[a[num[i]].to]=i+1;
	}
	for(int i=1;i<=q;i++)f[i+1]=f[i]+a[num[i]].w;
	for(int i=q;i>=1;i--)g[i]=g[i+1]+a[num[i]].w;
}
int main(){
	init();
	work();
    return 0;
}

$Update ext{于}2019.3.3$:

好像现在在$BZOJ$上可以过了。

但是复杂度有点玄学啊。。。

算了,复杂度$O( ext{能过})$。。。

过了就好。。。

各位巨佬千万别像我这个菜鸡一样。。。

原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9589270.html