51 nod 1443 路径和树
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
题解:
从rt开始的最短路径树顾名思义就是rt到其他节点的每条最短路径上的所有边构成的树。
这题一开始很容易想到,先跑一边最短路,然后再在这些边上最小生成树,但这样比较麻烦,而且容易T掉。
实际上只要在跑SPFA的时候顺便记录下每个点的最小前驱,最后答案求和就行了。
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=3e5+5; 4 const long long inf=1e18; 5 int n,m,edge,nxt[2*N],to[2*N],hea[N]; 6 long long val[2*N]; 7 void add(int u,int v,long long w) 8 { 9 to[++edge]=v; nxt[edge]=hea[u]; hea[u]=edge; val[edge]=w; 10 to[++edge]=u; nxt[edge]=hea[v]; hea[v]=edge; val[edge]=w; 11 } 12 long long dis[N],pre[N]; 13 bool vis[N]; 14 void spfa(int rt) 15 { 16 17 memset(vis,0,sizeof(vis)); 18 memset(pre,0,sizeof(pre)); 19 for (int i=1; i<=n; i++) dis[i]=inf; 20 int q[3*N]; 21 int h=0,t=1; 22 q[t]=rt; vis[rt]=true; dis[rt]=0; 23 24 while (h<t) 25 { 26 int x=q[++h]; vis[x]=false; 27 for (int i=hea[x]; i; i=nxt[i]) 28 { 29 if (dis[to[i]]>dis[x]+val[i]) 30 { 31 dis[to[i]]=dis[x]+val[i]; 32 pre[to[i]]=val[i]; 33 if (!vis[to[i]]) 34 { 35 vis[to[i]]=true; q[++t]=to[i]; 36 } 37 } 38 else if (dis[to[i]]==dis[x]+val[i]) pre[to[i]]=min(pre[to[i]],val[i]); 39 } 40 } 41 } 42 int main() 43 { 44 scanf("%d%d",&n,&m); 45 for (int i=1; i<=m; i++) 46 { 47 int u,v; long long w; 48 scanf("%d%d%lld",&u,&v,&w); 49 add(u,v,w); 50 } 51 int s; 52 scanf("%d",&s); 53 //cout<<"YES"<<endl; 54 spfa(s); 55 long long ans=0; 56 for (int i=1; i<=n; i++) 57 ans+=pre[i]; 58 printf("%lld ",ans); 59 return 0; 60 }
加油加油加油!!! fighting fighting fighting !!!