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
1 2 2
1 3 2
3 4 4
3 2 1
2 4 3
1 5
Sample Output
6
6
6
HINT
数据已加强By Vfleaking,另2017.9.1再加数据一组,未重测。
题解Here!
这个题自从2017.9.2的最后一个AC记录之后就没有人再AC了。。。
包括我也是。。。
这种无脑加强数据的题目基本上就是废题了。。。
当然,还可以到洛谷/LOJ上去搞搞事:
$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\n");
else printf("%d\n",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\text{于}2019.3.3$:
好像现在在$BZOJ$上可以过了。
但是复杂度有点玄学啊。。。
算了,复杂度$O(\text{能过})$。。。
过了就好。。。
各位巨佬千万别像我这个菜鸡一样。。。