并不是什么高端操作并且一些模型会用到
Description
给定一幅无向带权连通图G = (V, E) (这里V是点集,E是边集)。从点u开始的最短路径树是这样一幅图G1 = (V, E1),其中E1是E的子集,并且在G1中,u到所有其它点的最短路径与他在G中是一样的。
现在给定一幅无向带权连通图G和一个点u。你的任务是找出从u开始的最短路径树,并且这个树中所有边的权值之和要最小。
Input
单组测试数据。
第一行有两个整数n和m(1 ≤ n ≤ 3*10^5, 0 ≤ m ≤ 3*10^5),表示点和边的数目。
接下来m行,每行包含3个整数 ui, vi, wi ,表示ui和vi之间有一条权值为wi的无向边(1 ≤ ui,vi ≤ n, 1 ≤ wi ≤ 10^9)。
输入保证图是连通的。
最后一行给出一个整数u (1 ≤ u ≤ n),表示起点。
Output
输出这棵树的最小的权值之和。
Input示例
3 3
1 2 1
2 3 1
1 3 2
3
Output示例
2
题目大意
求最短路径树的最小权值和
题目分析
最短路径树是原图的一种生成树。注意以不同的点为根产生的最短路径树是不一样的(道理同最短路)。
这里要求的是“最小权值和”,听上去好像很麻烦的样子:要把跑的最短路的边拎出来,再做一遍最小生成树……
但是实际上我们发现它是满足贪心性质的,并且并不会影响后面元素的取值。
所以只需要维护一个$pre[i]$表示转移到$i$的最小边权,然后在dij过程中再加一句判断就可以了。
来自hzq的告诫:“能用堆优化dij就用堆优化的dij,SPFA尽量尽量不要写。系统堆优化的dij有这么难写吗?”
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 300035; 4 const int maxm = 600035; 5 const ll INF = 1152921504606846976; 6 7 ll dis[maxn],pre[maxn],ans; 8 struct cmp 9 { 10 bool operator ()(int a, int b) const 11 { 12 return dis[a] > dis[b]; 13 } 14 }; 15 struct Edge 16 { 17 int y; 18 ll val; 19 Edge(int a=0, ll b=0):y(a),val(b) {} 20 }edges[maxm]; 21 int n,m,s; 22 int head[maxn],nxt[maxm],edgeTot; 23 std::priority_queue<int, std::vector<int>, cmp> q; 24 25 int read() 26 { 27 char ch = getchar(); 28 int num = 0; 29 bool fl = 0; 30 for (; !isdigit(ch); ch = getchar()) 31 if (ch=='-') fl = 1; 32 for (; isdigit(ch); ch = getchar()) 33 num = (num<<1)+(num<<3)+ch-48; 34 if (fl) num = -num; 35 return num; 36 } 37 void addedge(int u, int v, ll w) 38 { 39 edges[++edgeTot] = Edge(v, w), nxt[edgeTot] = head[u], head[u] = edgeTot; 40 } 41 int main() 42 { 43 memset(head, -1, sizeof head); 44 n = read(), m = read(); 45 for (int i=1; i<=m; i++) 46 { 47 int u = read(), v = read(), w = read(); 48 addedge(u, v, w), addedge(v, u, w); 49 dis[i] = INF; 50 } 51 s = read(), q.push(s), dis[s] = 0; 52 while (q.size()) 53 { 54 int tt = q.top(); 55 q.pop(); 56 for (int i=head[tt]; i!=-1; i=nxt[i]) 57 { 58 int v = edges[i].y; 59 ll w = edges[i].val; 60 if (dis[v] > dis[tt]+w||(dis[v]==dis[tt]+w&&pre[v] > w)) 61 dis[v] = dis[tt]+w, pre[v] = w, q.push(v); 62 } 63 } 64 for (int i=1; i<=n; i++) 65 ans += pre[i]; 66 printf("%lld ",ans); 67 return 0; 68 }
END