最短路树
思路
对于最短路树上的任意非根节点 \(u\),\(dist(root,u)=\) \(root\) 到 \(u\) 的最短路。最短路树上的边一般是正权值的,因为如果是负权值好像建树的复杂度就高达 \(O(n^3)\) 了,因为 \(dijkstra\) 无法保证正确性,而 \(floyd\) 是 \(O(n^3)\) 的, \(SPFA\) 则是可以退化到 \(O(nm)\) 的。
构造时,跑一遍 \(dijkstra\),连边更新信息的两个点建树即可,\(root\) 即为出发点 \(u\)。很容易就可以理解这样会建成一棵树,因为一个点只会被更新一次。
最短路树一般用于最短路问题中有删边的问题。
例题一:\(CF1076D Edge Deletion\)
思路
问留下 \(k\) 条边以后最短路不变的点最多的情况。很容易想到用 \(root=1\) 的情况下构建一棵最短路树,然后只需要 \(n-1\) 条边就可以保证 \(n\) 个点的最短路不变。\(dfs\) 从 \(1\) 号节点开始记录一个 \(cnt\),只要 \(cnt\leq k\) 的就输出这条边,否则就直接 \(return\)。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int n, m, k;
int head[300005], e[600005], ne[600005], idx;
long long val[600005];
int pre[300005];
long long dist[300005];
bool vis[300005];
long long x, y, z;
int tot;
struct node{
long long d;
int p;
inline bool operator <(const node&a)const{
return d>a.d;
}
};
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)) x = (x<<1) + (x<<3) + (q^48), q = getchar();
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0) x = -x, putchar('-');
if(x > 9) write(x/10);
putchar(x%10^48);
}
inline void add(int u, int v, long long num){
e[++idx] = v, ne[idx] = head[u], val[idx] = num, head[u] = idx;
}
inline void dijkstra(){
priority_queue<node>q;
memset(dist, 0x7f, sizeof(dist));
q.push((node){0, 1});
dist[1] = 0;
while(!q.empty()){
int u = q.top().p;
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(register int i = head[u]; i; i = ne[i]){
int v = e[i];
if(dist[v] >= dist[u]+val[i]){
dist[v] = dist[u]+val[i];
q.push((node){dist[v], v});
pre[v] = i;
}
}
}
}
inline void dfs(int u){
for(register int i = head[u]; i; i = ne[i]){
int v = e[i];
if(i == pre[v]){
tot++;
if(tot > k || tot == n) exit(0);
write(i+1>>1);
putchar(' ');
dfs(v);
}
}
}
int main(){
read(n), read(m), read(k);
for(register int i = 1; i <= m; ++i){
read(x), read(y), read(z);
add(x, y, z);
add(y, x, z);
}
dijkstra();
write(min(n-1, k));
puts("");
dfs(1);
return 0;
}

浙公网安备 33010602011771号