[做題筆記] 那些未曾謀面的省選題
阿新 • • 發佈:2022-03-27
我的部落格大概要封筆了,最後一週也不會做什麼題了,再見了朋友們。
[HNOI2014] 道路堵塞
題目描述
解法
我們不妨考慮增量法,先把在最短路徑上的邊排除掉,跑完最短路之後再慢慢新增邊。
如果我們要求刪除邊 \(i\) 的答案,那麼我們需要新增邊 \([1,i)\),並且考慮 \((i,k]\) 邊的影響(這些邊我們是不加的),考慮把 \((i,k]\) 構成的路徑染色,那麼如果我們到達的某個點被染色,那麼可以直接走最短路到終點。
為了保證複雜度我們把給定的最短路徑染色,如果現在 \(\tt spfa\) 更新到了最短路上的第 \(i\) 個點,那麼我們這條路徑打上時間戳 \(i\)
時間複雜度基於 \(\tt spfa\),所以在不刻意卡的情況是可以通過的。
還有一種時間複雜度穩定的最短路樹做法,找機會填坑。
總結
圖論中的一些動態演算法十分重要,巧用動態演算法可以快速完成版本之間的轉化,以解決一維偏序關係。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int M = 200005; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,k,tot,f[M],id[M],d[M],in[M]; queue<int> q;int rd[M],p[M],g[M],ban[M]; struct edge{int v,c,next;}e[M]; struct node { int u,c; bool operator < (const node &b) const {return c>b.c;} };priority_queue<node> s; void spfa(int now) { q.push(now);in[now]=1; while(!q.empty()) { int u=q.front();q.pop();in[u]=0; for(int i=f[u];i;i=e[i].next) { int v=e[i].v,c=e[i].c; if(ban[i]) continue; if(d[v]>d[u]+c) { d[v]=d[u]+c; if(id[v]) s.push({id[v],d[v]+g[id[v]]}); else if(!in[v]) in[v]=1,q.push(v); } } } } signed main() { n=read();m=read();k=read(); for(int i=1;i<=m;i++) { int u=read(),v=read(),c=read(); e[++tot]=edge{v,c,f[u]},f[u]=tot; } for(int i=1;i<=k;i++) { rd[i]=read();ban[rd[i]]=1; p[i+1]=e[rd[i]].v;id[p[i+1]]=i+1; } p[1]=1;id[1]=1; for(int i=k;i>=1;i--) g[i]=g[i+1]+e[rd[i]].c; memset(d,0x3f,sizeof d); d[1]=0;spfa(1); for(int i=1;i<=k;i++) { while(!s.empty() && s.top().u<=i) s.pop(); if(s.empty()) puts("-1"); else printf("%d\n",s.top().c); d[p[i+1]]=d[p[i]]+e[rd[i]].c; spfa(p[i+1]); } }