求\(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即可。
同时,也存在非最短路的线性做法,见上面的题解。