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 "); 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{能过})$。。。
过了就好。。。
各位巨佬千万别像我这个菜鸡一样。。。