洛谷 P4180 【模板】严格次小生成树[BJWC2010]

题目描述

小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) 

这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

输入输出格式

输入格式:

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

 

输出格式:

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

 

输入输出样例

输入样例#1: 复制
5 6
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 
输出样例#1: 复制
11

说明

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

 

这道题直接说做法吧

首先我们需要知道一个性质 次小生成树的树边只和最小生成树有一边之差 别问我正确性我也不知道为什么 显然求证法...??

所以首先我们先cao一颗最小生成树出来 在考虑到底是哪条非树边替换哪条树边

所以枚举每一条非树边$(u, v)$ 在树上找到连接这两点的树链 寻找该链上的最大值 直接进行替换即可

为什么可以直接替换呢 因为既然他是最小生成树 那条链上的每条边都必然小于等于当前$(u, v)$ 但是会出现一个问题

题目要求的是 严格次小 若是最大值与$val(u, v)$相等 那么替换之后就不是严格次小了 所以要解决这个问题 不仅需要维护链上的最大值 还要维护次大值

若是$val(u, v) == max$ 那么我们就替换次大边即可 

可以使用树剖套线段树维护这个 倍增也可以

代码(树剖)

#include <bits/stdc++.h>
#define oo 1e18
using namespace std;

typedef long long ll;
const int N = 1e6 + 6;
int fa[N], head[N], nex[2 * N], tov[2 * N], val[2 * N], fat[N];
int tot, dep[N], son[N], size[N], in[N], idc, top[N], seq[N], w[N];
int n, m;
bool vis[3 * N];
ll sum = 0;

struct edge {
    int u, v, w;
}e[3 * N];

struct segment {
    int ma1, ma2;
}f[4 * N], ans;

bool cmp(const edge & a, const edge & b) {
    return a.w < b.w;
}

int find_fa(int u) {
    return u == fat[u] ? u : fat[u] = find_fa(fat[u]);
}

void add(int u, int v, int w) {
    
    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    val[tot] = w;
    head[u] = tot;
}

void dfs1(int u, int fat) {
    
    fa[u] = fat; size[u] = 1;
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if(v == fat) continue;
        dep[v] = dep[u] + 1;
        w[v] = val[i];
        dfs1(v, u); 
        size[u] += size[v];
        if(size[v] > size[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int tp) {
    
    in[u] = ++ idc; 
    top[u] = tp; seq[idc] = u;
    if(son[u]) dfs2(son[u], tp);
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if(v == fa[u]) continue;        
        if(v != son[u]) dfs2(v, v);
    }
}

void update(int o) {
    
    int ma = max(f[2 * o].ma1, f[2 * o + 1].ma1);
    f[o].ma1 = ma;
    f[o].ma2 = max((ma == f[2 * o].ma1 ? f[2 * o].ma2 : f[2 * o].ma1), 
    (ma == f[2 * o + 1].ma1 ? f[2 * o + 1].ma2 : f[2 * o + 1].ma1));
}

void build(int o, int l, int r) {
    
    if(l == r) { f[o].ma1 = w[seq[l]]; f[o].ma2 = -1; return ;}
    int mid = l + r >> 1;
    build(2 * o, l, mid); 
    build(2 * o + 1, mid + 1, r);
    update(o);
}

void Init( ) {
    
    scanf("%d%d",& n,& m);
    for(int i = 1;i <= m;i ++) 
        scanf("%d%d%d",& e[i].u, & e[i].v, & e[i].w);
    sort(e + 1, e + m + 1, cmp);
    for(int i = 1;i <= n;i ++) fat[i] = i;
    for(int i = 1;i <= m;i ++) {
        int fa1 = find_fa(e[i].u), fa2 = find_fa(e[i].v);
        if(fa1 == fa2) continue;
        fat[fa2] = fa1; vis[i] = true; sum += e[i].w;
        add(e[i].u, e[i].v, e[i].w); add(e[i].v, e[i].u, e[i].w);
    }
    dfs1(1, 0); dfs2(1, 0); build(1, 1, n);
}

void change(segment a) {
    
    int ma = max(a.ma1, ans.ma1);
    ans.ma2 = max((ans.ma1 == ma ? ans.ma2 : ans.ma1), (a.ma1 == ma ? a.ma2 : a.ma1));
    ans.ma1 = ma;
}

segment query(int o, int l, int r, int L, int R) {
    
    if(l >= L && r <= R) return f[o];
    int mid = l + r >> 1;
    if(R <= mid) return query(2 * o, l, mid, L, R);
    if(L > mid)  return query(2 * o + 1, mid + 1, r, L, R);
    segment q1 = query(2 * o, l, mid, L, R), q2 = query(2 * o + 1, mid + 1, r, L, R), q3;
    int ma = max(q1.ma1, q2.ma1); q3.ma1 = ma;
    q3.ma2 = max((ma == q1.ma1 ? q1.ma2 : q1.ma1), (ma == q2.ma1 ? q2.ma2 : q2.ma1));
    return q3;
}

segment Query(int u, int v) {
    
    ans.ma1 = 0, ans.ma2 = -1;
    while(top[u] != top[v]) {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        change(query(1, 1, n, in[top[u]], in[u]));
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    if(u != v) change(query(1, 1, n, in[v] + 1, in[u]));
    return ans;
}

void Solve( ) {
    
    ll Ans = oo;
    for(int i = 1;i <= m;i ++) {
        if(vis[i]) continue;
        segment ma = Query(e[i].u, e[i].v);
        ll del;
        if(ma.ma1 == e[i].w) del = ma.ma2; else del = ma.ma1;
        if(del == -1) continue;
        Ans = min(Ans, sum - del + 1ll * e[i].w);
    }
    printf("%lld\n", Ans);
}

int main( ) {
    
    Init( );
    Solve( );
}
posted @ 2018-10-23 12:08  阿澈说他也想好好学习  阅读(127)  评论(0编辑  收藏  举报