题意与分析
题意是这样的,定义一个从某点出发的所有最短路方案中,选择边权和最小的最短路方案,称为最短生成树。
现在求一棵最短生成树,输出总边权和与选取边的编号。
我们首先要明白这样一个结论:对一个图求Dijkstra后,把所有得到的最短路边全部连起来,生成的图一定是一棵树,是不会有环的。原因自己推一下就可以感受到。
那么这样一来,这个树相当于我们在Dijkstra的时候就已经得到了。记录边是Dijkstra的基本操作,而我们只需要考虑一下当最短路相等时谁更优的情况并更新就可以了。
代码
#include <bits/stdc++.h>
#define MP make_pair
#define PB emplace_back
#define fi first
#define se second
#define ZERO(x) memset((x), 0, sizeof(x))
#define ALL(x) (x).begin(),(x).end()
#define rep(i, a, b) for (repType i = (a); i <= (b); ++i)
#define per(i, a, b) for (repType i = (a); i >= (b); --i)
#define QUICKIO
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
using namespace std;
using ll=long long;
using repType=int;
struct Edge
{
int u, v;
ll w;
Edge() {}
Edge(int _u, int _v, ll _w): u(_u), v(_v), w(_w) {}
bool
operator < (const Edge& rhs) const
{
if(w==rhs.w)
{ return (u==rhs.u)?v<rhs.v:u<rhs.u; }
else { return w<rhs.w; }
}
};
const int MAXN=300005;
vector<Edge> edges;
vector<int> G[MAXN];
void
add_edge(int u, int v, ll w)
{
edges.PB(u, v, w);
G[u].PB(int(edges.size())-1);
}
ll dist[MAXN];
int pre[MAXN]; // pre: last edge
void
dijkstra(int start)
{
memset(pre, -1, sizeof(pre));
memset(dist, 0x3f, sizeof(dist));
using P=pair<ll, int>;
priority_queue<P, vector<P>, greater<> > pq; // <dist, pnt>: 大根堆
dist[start]=0;
pq.push(MP(0, start));
while(!pq.empty())
{
auto now=pq.top(); pq.pop();
int u=now.se;
if(dist[u]<now.fi) { continue; }
rep(i, 0, int(G[u].size())-1)
{
int v=edges[G[u][i]].v;
ll w=edges[G[u][i]].w;
if(dist[v]>dist[u]+w)
{
dist[v]=dist[u]+w;
pre[v]=G[u][i];
pq.push(MP(dist[v], v));
}
else if(dist[v]==dist[u]+w && edges[pre[v]].w>edges[G[u][i]].w)
{ pre[v]=G[u][i]; }
}
}
}
int
main()
{
int n, m;
scanf("%d%d", &n, &m);
rep(i, 1, m)
{
int u, v;
ll w;
scanf("%d%d%lld", &u, &v, &w);
add_edge(u, v, w);
add_edge(v, u, w);
}
int stp; scanf("%d", &stp);
dijkstra(stp);
ll ans=0;
rep(i, 1, n) if(i!=stp) { ans+=edges[pre[i]].w; }
printf("%lld
", ans);
rep(i, 1, n) if(i!=stp) { printf("%d ", pre[i]/2+1); }
printf("
");
return 0;
}