1977: [BeiJing2010组队]次小生成树 Tree
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 4005 Solved: 1161
[Submit][Status][Discuss]
Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
HINT
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
Source
一个明显的定义:
次小生成树一定是最小生成树中的某一条边“走错了”的产物
由此衍生出一个构造法:枚举剩余的m - n + 1条边, E(u, v),在最小生成树上的u, v的链上删去最大的那条边,加入新边
所有这样构造的树中最小的就是次小生成树
但是本题是严格次小,所以还要维护严格次大边
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100100;
const int MAXE = 300300;
const int INF = 0x3f3f3f3f;
template <typename T> inline void read(T &x) {
int c = getchar();
bool f = false;
for (x = 0; !isdigit(c); c = getchar()) {
if (c == '-') {
f = true;
}
}
for (; isdigit(c); c = getchar()) {
x = x * 10 + c - '0';
}
if (f) {
x = -x;
}
}
struct edge {
int u, v;
int len;
edge(int _u = 0, int _v = 0, int _l = 0) : u(_u), v(_v), len(_l) {}
}e[MAXE], e2[MAXE];
int n, m, tot, tote;
bool operator < (edge a, edge b) {
return a.len < b.len;
}
int Fa[MAXN], rnk[MAXN], cnt;
int Find(int x) {
return x == Fa[x] ? x : Fa[x] = Find(Fa[x]);
}
void unite(int x, int y) {
int f1 = Find(x), f2 = Find(y);
if(rnk[f1] < rnk[f2]) Fa[f1] = f2;
else {
Fa[f2] = f1;
if(rnk[f1] == rnk[y]) rnk[f1]++;
}
}
struct Edge {
int to, nxt, len;
Edge() {}
Edge(int _to, int _nxt, int _len) : to(_to), nxt(_nxt), len(_len){}
}E[MAXN << 1];
int h[MAXN], Lr[MAXN], Rr[MAXN], val[MAXN];
int sz[MAXN], son[MAXN], fa[MAXN], dep[MAXN], top[MAXN];
inline void add_edge(int u, int v, int w) {
E[++cnt] = Edge(v, h[u], w), h[u] = cnt;
E[++cnt] = Edge(u, h[v], w), h[v] = cnt;
}
void dfs1(int x) {
sz[x] = 1; dep[x] = dep[fa[x]] + 1;
for(int i = h[x]; i; i = E[i].nxt) {
int to = E[i].to;
if(to == fa[x]) continue;
fa[to] = x; val[to] = E[i].len;
dfs1(to);
sz[x] += sz[to];
if(sz[to] > sz[son[x]]) son[x] = to;
}
}
void dfs2(int x) {
Lr[x] = ++tot;
Rr[Lr[x]] = val[x];
if(x == son[fa[x]]) top[x] = top[fa[x]];
else top[x] = x;
if(son[x]) dfs2(son[x]);
for(int i = h[x]; i; i = E[i].nxt) {
int to = E[i].to;
if(to == fa[x] || to == son[x]) continue;
dfs2(to);
}
}
struct SegmentTree {
int l, r;
int val;
SegmentTree() {
l = r = 0;
val = -INF;
}
}tre[MAXN << 2];
#define Ls(x) x << 1
#define Rs(x) x << 1 | 1
void push_up(int x) {
tre[x].val = max(tre[Ls(x)].val, tre[Rs(x)].val);
}
void build(int x, int l, int r) {
tre[x].l = l, tre[x].r = r;
if(l == r) {
tre[x].val = Rr[l];
return ;
}
int mid = (l + r) >> 1;
build(Ls(x), l, mid);
build(Rs(x), mid + 1, r);
push_up(x);
}
typedef pair<int, int> pii;
#define mp(a, b) make_pair(a, b)
#define X first
#define Y second
pair<int, int> query(int x, int L, int R) {
int l = tre[x].l, r = tre[x].r;
if(L <= l && R >= r) return make_pair(tre[x].val, -INF);
if(L > r || l > R) return make_pair(-INF, -INF - 1);
int mid = (l + r) >> 1;
pair<int, int> ret = make_pair(-INF, -INF - 1);
if(mid >= L) {
pii tmp = query(Ls(x), L, R);
if(tmp.X > ret.X) ret.Y = ret.X, ret.X = tmp.X;
if(tmp.Y > ret.Y && tmp.Y != ret.X) ret.Y = tmp.Y;
if(tmp.X < ret.X && tmp.X > ret.Y) ret.Y = tmp.X;
}
if(mid < R) {
pii tmp = query(Rs(x), L, R);
if(tmp.X > ret.X) ret.Y = ret.X, ret.X = tmp.X;
if(tmp.Y > ret.Y && tmp.Y != ret.X) ret.Y = tmp.Y;
if(tmp.X < ret.X && tmp.X > ret.Y) ret.Y = tmp.X;
}
return ret;
}
pii qmax(int u, int v) {
int f1 = top[u], f2 = top[v];
pii ans = mp(-INF, -INF);
while(f1 != f2) {
if(dep[f1] < dep[f2]) {
swap(f1, f2);
swap(u, v);
}
pii tmp = query(1, Lr[f1], Lr[u]);
if(tmp.X > ans.X) ans.Y = ans.X, ans.X = tmp.X;
if(tmp.Y > ans.Y && tmp.Y != ans.X) ans.Y = tmp.Y;
if(tmp.X < ans.X && tmp.X > ans.Y) ans.Y = tmp.X;
u = fa[f1];
f1 = top[u];
}
if(u == v) return ans;
if(dep[u] > dep[v]) swap(u, v);
pii tmp = query(1, Lr[son[u]], Lr[v]);
if(tmp.X > ans.X) ans.Y = ans.X, ans.X = tmp.X;
if(tmp.Y > ans.Y && tmp.Y != ans.X) ans.Y = tmp.Y;
if(tmp.X < ans.X && tmp.X > ans.Y) ans.Y = tmp.X;
return ans;
}
#define LL long long
signed main() {
read(n); read(m);
for(int i = 1; i <= m; i++) {
int a, b, c;
read(a), read(b), read(c);
e[i] = edge(a, b, c);
}
sort(e + 1, e + m + 1);
long long sum = 0;
for(int i = 1; i <= n; i++) Fa[i] = i;
for(int i = 1; i <= m; i++) {
int u = e[i].u;
int v = e[i].v;
int len = e[i].len;
if(Find(u) != Find(v)) {
add_edge(u, v, len);
unite(u, v);
sum += (LL) len;
}
else {
e2[++tote] = edge(u, v, len);
}
}
dfs1(1), dfs2(1);
build(1, 1, n);
LL ans = INF;
ans *= 1000000;
for(int i = 1; i <= tote; i++) {
int u = e2[i].u;
int v = e2[i].v;
int len = e2[i].len;
pii lm = qmax(u, v);
if(sum < sum - (LL)lm.X + (LL)len)
ans = min(ans, sum - (LL)lm.X + (LL)len);
else ans = min(ans, sum - (LL)lm.Y + (LL)len);
}
printf("%lld\n", ans);
return 0;
}