Description

给定一幅无向带权连通图G = (V, E) (这里V是点集,E是边集)。从点u开始的最短路径树是这样一幅图G1 = (V, E1),其中E1是E的子集,并且在G1中,u到所有其它点的最短路径与他在G中是一样的。
现在给定一幅无向带权连通图G和一个点u。你的任务是找出从u开始的最短路径树,并且这个树中所有边的权值之和要最小。
n,m<=300000

Solution

首先把跑一遍最短路,把最短路上的边标出来构成最短路图,明显它是有向无环的

大贪心:除起点外每个点选择边权最小的一条入边,加起来就是答案

证明:
为什么这是最优的?
最短路径树中每个点有且仅有一个祖先,每个点都选边权最小的一条一定是最优的

为什么这是一棵树?
首先我们选了n-1条边,又因为这是一个有向无环图,不可能构成环,所以它必定是一个连通图,也就是一棵树

Code

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
#define LL long long
using namespace std;
int fs[N],nt[2*N],dt[2*N],pr[2*N],s1[N],n,m,m1,st,d[5*N];
LL dis[N];
bool bz[N];
void link(int x,int y,int z)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
pr[m1]=z;
}
void spfa()
{
int l=0,r=1;
d[r]=st;
memset(dis,107,sizeof(dis));
dis[st]=0;
bz[st]=1;
while(l<r)
{
int k=d[++l];
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(dis[k]+pr[i]<dis[p])
{
dis[p]=dis[k]+pr[i];
if(!bz[p]) d[++r]=p,bz[p]=1;
}
}
bz[k]=0;
}
}
void bfs()
{
int l=0,r=1;
d[r]=st;
memset(bz,0,sizeof(bz));
bz[st]=1;
while(l<r)
{
int k=d[++l];
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(dis[k]+pr[i]==dis[p])
{
s1[p]=min(s1[p],pr[i]);
if(!bz[p]) d[++r]=p,bz[p]=1;
}
}
}
}
int main()
{
cin>>n>>m;
fo(i,1,m)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
link(x,y,z),link(y,x,z);
}
cin>>st;
memset(s1,107,sizeof(s1));
spfa();
bfs();
LL v=0;
fo(i,1,n) if(i!=st) v+=s1[i];
printf("%lld\n",v);
}