3575: [Hnoi2014]道路堵塞
Time Limit: 10 Sec Memory Limit: 128 MB
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的由交通部指定的最短路径上的道路的编号。
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
100%的数据满足2<N<100000,1<M<200000。所用道路长度大于0小于10000。
数据已加强By Vfleaking
你看见我的跳蚤国王了吗?哦它被FFF了?资磁!
这题真是火的冒烟,劲爆无比,谁也想不到正解来自一个很扯淡的证明。
正解:SPFA鬼畜技巧。
因为它已经钦定了最短路,而且要对最短路上每一条边输出一次答案,所以很容易想到正解是枚举BAN哪条边再鬼一鬼。
这里有个十分扯淡的证明:新·最短路一腚是S->之前最短路上某一点p->之前最短路上某一点q->T;
感觉并不知道怎么证明是吧,但如果我说S和p,q和T 可以为同一个点呢?
那么所有的情况都被包含了是的吗?没错是的。您强×把答案放在了一个鬼证明上。
所以按照这个证明的思想,我们枚举删掉最短路上的哪条边,然后把这条边的s加进队列做SPFA。
但是这样不加优化的做法会让你过吗?答案是显然否定的。那么来点更劲的:
先把最短路上的所有边都BAN掉,然后从S到T开始恢复边,并把s丢进队列。
然后每次就可以不清空dis数组了,因为你之前SPFA的结果你都可以调用以前的结果。
然后统计答案就是把走到的钦定最短路上的点的距离加上它到T的距离取个min就好了。这里有一个玄妙的做法就是把答案加上这个点放成结构体扔进堆里,每次弹出到达点在s之前的点因为这是在这条路被BAN的时候SPFA到的,不一定是当前的合法方案,也不会比最优答案更好。所以判完-1后,第一个在s之后的点就不管是不是s更新的点了,却又是一个合法的点,就避免了很多麻烦。
代码还是很好看懂的。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define dob double using namespace std; const int N = 200010; struct Node{int from,to,val,next;}E[N]; struct Data{int p,val;bool operator <(const Data &b)const{return val>b.val;}}; int n,m,L,head[N],tot,ban[N],rk[N],In[N],far[N],dis[N],fr[N],s[N]; queue<int>Q;priority_queue<Data>T; int gi() { int x=0,res=1;char ch=getchar(); while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)res*=-1;ch=getchar();} while(ch<=‘9‘&&ch>=‘0‘)x=x*10+ch-48,ch=getchar(); return x*res; } inline void link(int u,int v,int c) { E[++tot]=(Node){u,v,c,head[u]}; head[u]=tot; } inline void SPFA(int st) { queue<int>Q;Q.push(st); while(!Q.empty()){ int x=Q.front();Q.pop();In[x]=0; for(int e=head[x];e;e=E[e].next){ if(ban[e])continue;int y=E[e].to; if(far[x]+E[e].val<far[y]){ far[y]=far[x]+E[e].val; if(fr[y])T.push((Data){fr[y],far[y]+dis[fr[y]]}); else if(!In[y])Q.push(In[y]=y); } } } } int main() { n=gi();m=gi();L=gi(); for(int i=1;i<=m;++i){ int u=gi(),v=gi(),c=gi(); link(u,v,c); } s[1]=1;fr[1]=1; for(int i=1;i<=L;++i){ ban[rk[i]=gi()]=1; s[i+1]=E[rk[i]].to; fr[s[i+1]]=i+1; } for(int i=L;i;--i) dis[i]=dis[i+1]+E[rk[i]].val; memset(far,127/3,sizeof(far));far[1]=0; SPFA(1); for(int i=1;i<=L;++i){ while(!T.empty() && T.top().p<=i)T.pop(); if(T.empty())printf("-1\n"); else printf("%d\n",T.top().val); far[E[rk[i]].to]=far[s[i]]+E[rk[i]].val; SPFA(s[i+1]); } return 0; }
这还真TMD妙不可言,玄之又玄,精妙绝伦啊(伦boy:嗯?)。