bzoj1977: [BeiJing2010组队]次小生成树 Tree(严格次小生成树 树链剖分+线段树)

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值