RT,代码如下,已经尽可能地写好注释了:
#include <bits/stdc++.h>
using namespace std;
#define DEBUG 0
#define int long long
#if DEBUG == 0
#define MAXN 50005
#define MAXM 500005
#else
#define MAXN 2005
#define MAXM 2005
#endif
struct Edge {
int u, v, w;
};
struct ToNode {
int t, w;
};
int n, m, s, k;
Edge e[MAXM];
int fa[MAXN];
bool ac[MAXM];
vector<ToNode> g[MAXN];
bool vis[MAXN];
void init(int x) { // 并查集 - 初始化
for(int i = 1; i <= x; i++) {
fa[i] = i;
}
}
int find(int x) { // 并查集 - 查找
if(fa[x] == x) {
return x;
} else {
return fa[x] = find(fa[x]);
}
}
void merge(int x, int y) { // 并查集 - 合并
fa[find(x)] = find(y);
}
void dfs(int x) { // 将x所在的连通块中的每一个节点都标记为「已访问」
vis[x] = 1;
for(ToNode &i : g[x]) {
if(vis[i.t]) {
continue;
}
dfs(i.t);
}
}
bool cmpE(const Edge &_a, const Edge &_b) { // 用于Edge的比较函数
if((_a.u == s) != (_b.u == s)) { // 如果有一条边是从s发出的
return (_a.u == s) > (_b.u == s); // 那么这条边靠前排
} else { // 其他情况
return _a.w < _b.w; // 按照边权排序
}
}
bool cmpTN(const ToNode &_a, const ToNode &_b) { // 用于ToNode的比较函数
return _a.w < _b.w;
}
signed main() {
scanf("%lld%lld%lld%lld", &n, &m, &s, &k);
for(int i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &e[i].u, &e[i].v, &e[i].w);
if(e[i].v == s) {
swap(e[i].u, e[i].v); // 调整顺序,使得s节点总是为起始点
}
if(e[i].u != s) {
g[e[i].u].push_back({e[i].v, e[i].w});
g[e[i].v].push_back({e[i].u, e[i].w}); // 记录双向边
} else {
g[e[i].u].push_back({e[i].v, e[i].w}); // 只添加从s出发的边,防止下面dfs的时候遍历到节点s
}
}
init(n); // 初始化并查集
int cnts = 0; // cnts记录当前已经添加的「从s发出的边数」
int cnt = 0; // cnt记录当前已经添加的「总边数」
int ans = 0;
sort(g[s].begin(), g[s].end(), cmpTN); // 对从s发出的所有边按边的权值排序
for(ToNode &i : g[s]) { // 遍历从s出发的边
if(!vis[i.t]) { // 所在连通块没有被标记过
dfs(i.t); // 标记整个连通块
cnt++;
cnts++;
ans += i.w; // 记录答案
merge(1, i.t); // 添加这条边
}
}
sort(e + 1, e + m + 1, cmpE); // 对剩余的边排序
for(int i = 1; i <= m; i++) { // Kruskal
if(find(e[i].u) != find(e[i].v)) {
if(e[i].u == s && cnts >= k) { // 如果这条边是从s发出且从s节点发出的边已经足够了
continue; // 那么忽略这条边
}
merge(e[i].u, e[i].v);
cnt++;
ans += e[i].w; // 记录答案
if(e[i].u == s) { // 若这条边是从s发出的,则记录
cnts++;
}
}
}
if(cnt < n - 1 || cnts != k) { // 不是一棵树 或者 从s发出的边不足k条
puts("Impossible"); // It's IMPOSSIBLE!
} else {
printf("%lld\n", ans); // 答案
}
}