题意:给你一个有向图,让你选择其中的一些边,使得图联通,并且权值最小,如果几个点形成强联通分量,则里面的边权值为零。
思路:首先强联通缩点,然后剩下的就是一个DAG了,如果这个DAG联通并且权值和最小,则除了起点每个点的入度一定为1,对于每一个点,我们只需要选择以它为终点的边中权值最小的那条即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define REP( i, a, b ) for( int i = a; i < b; i++ )
#define CLR( a , x ) memset( a , x , sizeof a )
const int maxn = 50000 + 10;
const int maxe = 100000 + 10;
struct Edge{
int v , c, next;
Edge (int v = 0, int c = 0, int next = 0) : v(v), c(c), next(next) {}
};
struct SCC{
int Head[maxn], cntE;
int dfn[maxn], low[maxn], dfs_clock;
int scc[maxn], scc_cnt;
int Stack[maxn], top;
bool ins[maxn];
Edge edge[maxe];
void init(){
top = 0;
cntE = 0;
scc_cnt = 0;
dfs_clock = 0;
CLR(ins, 0);
CLR(dfn, 0);
CLR(Head, -1);
}
void add(int u, int v, int c){
edge[cntE] = Edge( v, c, Head[u]);
Head[u] = cntE++;
}
void Tarjan(int u){
dfn[u] = low[u] = ++dfs_clock;
Stack[top++] = u;
ins[u] = 1;
for (int i = Head[u] ; ~i ; i = edge[i].next){
int v = edge[i].v;
if (!dfn[v]){
Tarjan (v) ;
low[u] = min(low[u], low[v]) ;
}
else if (ins[v])
low[u] = min (low[u], dfn[v]) ;
}
if (low[u] == dfn[u]){
++scc_cnt;
while ( 1 ){
int v = Stack[--top];
ins[v] = 0;
scc[v] = scc_cnt;
if (v == u)
break;
}
}
}
void find_scc(int n){
REP(i, 0, n) if(!dfn[i]) Tarjan (i) ;
}
}scc;
int n, m;
vector<int> G[maxn];
void solve(){
scc.init();
for(int i = 0; i < m; i++){
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
scc.add(u, v, c);
}
scc.find_scc(n);
for(int i = 1; i <= scc.scc_cnt; i++) G[i].clear();
for(int i = 0; i < n; i++)
for(int j = scc.Head[i]; ~j; j = scc.edge[j].next){
int u = i, v = scc.edge[j].v;
if(scc.scc[u] != scc.scc[v]){
G[scc.scc[v]].push_back(scc.edge[j].c);
}
}
int ans = 0;
for(int i = 1; i <= scc.scc_cnt; i++){
int cur_ans = 1e9;
if(i == scc.scc[0]) continue;
for(int j = 0; j < (int)G[i].size(); j++)
cur_ans = min(cur_ans, G[i][j]);
ans += cur_ans;
}
printf("%d\n", ans);
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF) solve();
return 0;
}