求\(s\)到\(t\)必須經過某個點/某條邊的最短路
這個相當板子了,點\(u\)的答案是\(dis(s,u)+dis(u,t)\),邊\(e=(u,v)\)的答案是\(\min(dis(s,u)+dis(v,t),dis(s,v)+dis(u,t))+w(e)\)。其中\(dis(u,v)\)表示\(u\)到\(v\)的最短路。
從\(s\)和\(t\)各跑一次Dijkstra,其中\(t\)用反圖。預處理出從\(s\)出發和以\(t\)結束的最短路即可。
求最短路數量
P2047 [NOI2007] 社交網路
- 對於Dijkstra,在鬆弛時,如果\(d[u]+w=d[i]\),則令\(cnt[i]\leftarrow cnt[i]+cnt[u]\);否則令\(cnt[i]\leftarrow cnt[u]\)。
- 對於Floyd,列舉\(i\)到\(j\)的中轉點\(k\),如果\(d[i][k]+d[k][j]=d[i][j]\),則令\(cnt[i][j]\leftarrow cnt[i][j]+cnt[i][k]\times cnt[k][j]\);否則令\(cnt[i][j]\leftarrow cnt[i][k]\times cnt[k][j]\)。
- 說明:列舉\(k\)之前的最短路經過的結點都只經過\(1\sim (k-1)\)的點,所以不會重複統計。
Dijkstra 堆最佳化
struct edge{int to,w;};
vector<edge> G[N];
int d[N],cnt[N];
priority_queue<PII,vector<PII>,greater<PII>> q;
void dijkstra(int s){
memset(d,0x3f,sizeof d);
memset(cnt,0,sizeof cnt);
d[s]=0,cnt[s]=1,q.push({0,s});
while(!q.empty()){
auto t=q.top();
int u=t.second;
q.pop();
if(t.first>d[u]) continue;
for(auto i:G[u]){
if(d[u]+i.w==d[i.to])
cnt[i.to]+=cnt[u];
if(d[u]+i.w<d[i.to]){
d[i.to]=d[u]+i.w;
q.push({d[i.to],i.to});
cnt[i.to]=cnt[u];
}
}
}
}
Floyd
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(f[i][k]+f[k][j]==f[i][j])
cnt[i][j]+=cnt[i][k]*cnt[k][j];
else if(f[i][k]+f[k][j]<f[i][j])
f[i][j]=f[i][k]+f[k][j],
cnt[i][j]=cnt[i][k]*cnt[k][j];
求兩點間最大邊權的最小值
並不是例題:P2245 星際導航
P2245的雙倍經驗:P1967 [NOIP2013 提高組] 貨車運輸
P2245的正解是(Kruskal重構樹 / 最小生成樹) + LCA,這樣時間複雜度是\(O(m\log m)+O(n\log n)+O(q\log n)\),每次詢問是\(O(\log n)\)的(當然也LCA可以離線求,複雜度上會更優一些)。
上面的題用最短路無法透過,但是在固定起點的情況下,Dijkstra可以做到\(O(m\log m)\)預處理,\(O(1)\)查詢;需要任意兩點間的答案時,Floyd可以\(O(n^3)\)預處理,\(O(1)\)查詢。
- 對於Dijkstra,重新定義\(f[x]\)為到\(x\)的最大邊權的最小值。鬆弛時,\(f[i]=\min(f[i],\max(f[u],w))\)。
- 初始化時\(f\)設為\(+\infty\),\(f[s]=-\infty\),優先佇列按升序。如果是最小邊權最大則反過來。
- 對於Floyd,重新定義\(f[i][j]\)為\(i\)到\(j\)的最大邊權的最小值。轉移時\(f[i][j]=\min(\max(f[i][k],f[k][j]))\)。
- 鄰接矩陣無邊處置為\(+\infty\)。如果是最小邊權最大則反過來。
求每個點到最近關鍵點(可能有多個)的距離
P9432 [NAPC-#1] rStage5 - Hard Conveyors ~ 題解
初始化時,將所有關鍵點的\(d\)設為\(0\),其他設為\(+\infty\),正常跑Dijkstra即可。
同時,也存在非最短路的線性做法,見上面的題解。