【BJWC2010】次小生成树

题目链接:https://www.luogu.com.cn/problem/P4180

题目大意:给定一张含有 \(m\) 条边的无向带权图 , 求出这张图中边权之和严格大于最小生成树的次小生成树的边权之和

solution

笔者太鸽了 , 一连咕了三天 , 因此来补一下前两天的锅

这道题的思路很显然 , 先求出这张图的最小生成树 , 记它的边权之和为\(sum\) , 再考虑剩下的 \(m - n + 1\) 条边 , 如果把第 \(i\) 条, 分别连接\(a_i , b_i\), 权值为\(w_i\)的边加入树中 , 显然组成的生成树的最小值为 \(sum - max\left\{dist(u, v)\right\} + w_i\) (其中 \(u\) , \(v\) 为 \(path( a_i , b_i )\) 上的点) , 但要注意 , 这道题要求的是严格次小生成树 , 如果 \(max\left\{dist(u, v)\right\} = w_i\) 的话 , 求出的还是最小生成树 , 对于这种情况 , 还需要求出 \(max_{second}\left\{dist(u, v)\right\}\) , 组成的生成树最小值为 \(sum - max_{second}\left\{dist(u, v)\right\} + w_i\) , 最后比较每条边组成的生成树大小 , 取其中的最小值即可

对于求 \(max\left\{dist(u, v)\right\} = w_i\) 以及 \(max_{second}\left\{dist(u, v)\right\}\) 的值 , 可以分别用倍增处理 , 十分套路 , 笔者就不加赘述了

时间复杂度 : \(O(mlogn)\)

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
    int RR = 1; FF = 0; char CH = getchar();
    for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
    for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
    FF *= RR;
}
inline void file(string str) {
    freopen((str + ".in").c_str(), "r", stdin);
    freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 1e5 + 10, Log = 21, M = 3e5 + 10;
int n, m, mf[N], fa[N][Log + 1];
long long g[N][Log + 1][2], wi[N << 1], ans = LONG_LONG_MAX, sum;
int now, fst[N], nxt[N << 1], num[N << 1], dep[N], fl[M];
struct edge{
    int u, v, w;
    friend bool operator < (edge ai, edge bi) {
        return ai.w < bi.w;
    }
}path[M];
int get_fa(int xi) {
    return mf[xi] == xi ? xi : mf[xi] = get_fa(mf[xi]);
}
void add(int u, int v, int w) {
    nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
    nxt[++now] = fst[v], fst[v] = now, num[now] = u, wi[now] = w;
}
void pre_bz(int xi) {
    dep[xi] = dep[fa[xi][0]] + 1;
    for(int i = 1; i <= Log; i++) {
        fa[xi][i] = fa[fa[xi][i - 1]][i - 1];
        g[xi][i][0] = max(g[xi][i - 1][0], g[fa[xi][i - 1]][i - 1][0]);
        if(g[xi][i - 1][0] == g[fa[xi][i - 1]][i - 1][0])
            g[xi][i][1] = max(g[xi][i - 1][1], g[fa[xi][i - 1]][i - 1][1]);
        else if(g[xi][i - 1][0] > g[fa[xi][i - 1]][i - 1][0])
            g[xi][i][1] = max(g[xi][i - 1][1], g[fa[xi][i - 1]][i - 1][0]);
        else g[xi][i][1] = max(g[xi][i - 1][0], g[fa[xi][i - 1]][i - 1][1]);
    }
    for(int i = fst[xi]; i; i = nxt[i])
        if(num[i] != fa[xi][0]) {
            fa[num[i]][0] = xi;
            g[num[i]][0][0] = wi[i], g[num[i]][0][1] = LONG_LONG_MIN;
            pre_bz(num[i]);
        }
}
pair<long long, long long> fmax(int xi, int yi) {
    pair<long long, long long> mans;
    if(dep[xi] < dep[yi]) swap(xi, yi);
    for(int i = Log; i >= 0; i--)
        if(dep[fa[xi][i]] >= dep[yi]) {
            if(mans.first < g[xi][i][0]) {
                mans.second = max(mans.first, g[xi][i][1]);
                mans.first = g[xi][i][0];
            }
            else if(mans.first > g[xi][i][0])
                mans.second = max(mans.second, g[xi][i][0]);
            else mans.second = max(mans.second, g[xi][i][1]);
            xi = fa[xi][i]; //把往上跳这一步给漏了都有60pts
        }
    if(xi == yi) return mans;
    for(int i = Log; i >= 0; i--)
        if(fa[xi][i] != fa[yi][i]) {
            if(mans.first < g[xi][i][0]) {
                mans.second = max(mans.first, g[xi][i][1]);
                mans.first = g[xi][i][0];
            }
            else if(mans.first > g[xi][i][0])
                mans.second = max(mans.second, g[xi][i][0]);
            else mans.second = max(mans.second, g[xi][i][1]);
            if(mans.first < g[yi][i][0]) {
                mans.second = max(mans.first, g[yi][i][1]);
                mans.first = g[yi][i][0];
            }
            else if(mans.first > g[yi][i][0])
                mans.second = max(mans.second, g[yi][i][0]);
            else mans.second = max(mans.second, g[yi][i][1]);
            xi = fa[xi][i], yi = fa[yi][i]; //这一步也是
        }
    if(g[xi][0][0] > mans.first)
        mans.second = mans.first, mans.first = g[xi][0][0];
    else if(g[xi][0][0] < mans.first)
        mans.second = max(mans.second, g[xi][0][0]);
    if(g[yi][0][0] > mans.first)
        mans.second = mans.first, mans.first = g[yi][0][0];
    else if(g[yi][0][0] < mans.first)
        mans.second = max(mans.second, g[yi][0][0]);
    return mans;
}
int main() {
    //file("");
    read(n), read(m);
    for(int i = 1; i <= m; i++)
        read(path[i].u), read(path[i].v), read(path[i].w);
    sort(path + 1, path + m + 1);
    for(int i = 1; i <= n; i++) mf[i] = i;
    for(int i = 1; i <= m; i++) {
        if(get_fa(path[i].v) == get_fa(path[i].u)) {
            fl[i] = true;
            continue;
        }
        sum += path[i].w;
        add(path[i].u, path[i].v, path[i].w);
        mf[get_fa(path[i].v)] = get_fa(path[i].u);
    }
    g[1][0][1] = LONG_LONG_MIN;
    pre_bz(1);
    for(int i = 1; i <= m; i++)
        if(fl[i]) {
            pair<long long, long long> res = fmax(path[i].u, path[i].v);
            if(path[i].w > res.first) ans = min(ans, sum - res.first + path[i].w);
            else ans = min(ans, sum - res.second + path[i].w);
        }
    cout << ans << endl;
    return 0;
}

原文地址:https://www.cnblogs.com/magicduck/p/12253040.html

时间: 2024-10-09 12:40:52

【BJWC2010】次小生成树的相关文章

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

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

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

严格次小生成树模板 算法流程: 先用克鲁斯卡尔求最小生成树,然后给这个最小生成树树剖一下,维护边权转点权,维护最大值和严格次大值. 然后枚举没有被选入最小生成树的边,在最小生成树上查一下这条边的两端点的路径上的最长边,如果最长边等于枚举到的边的边权,那么选次长边(没有次长边的话直接跳过),然后在最小生成树的权值上减去路径上最/次长边,加上当前枚举的边的边权 因为如果加入枚举的边的,那么就形成了一个环,需要断开一条边 注意一开始单点次小值赋为0 #include<iostream> #inclu

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

传送门 次小生成树 那肯定是最小生成树上改一条边(改两条肯定不如只改其中一条小) 那就枚举所有不在最小生成树上的边 考虑如果把此边加入,另一边删除后的情况 考虑要删哪条边后才能保持树的形态,并且总长最小 加入一条边后树就会出现一个环 那么删掉的边要在加入的边连接的两点间的路径上 并且删去的边要尽量大 那就可以用LCA来求出树上两点间路径上的最长边 但是现在还有一个问题,可能删去的边和加入的边一样长 所以还要维护一下次长的边 次长边维护也不难,具体看代码 #include<iostream> #

P4180 严格次小生成树[BJWC2010]

题目链接 https://www.luogu.org/problemnew/show/P4180 题目描述 小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e

P4180 严格次小生成树[BJWC2010] Kruskal,倍增

题目链接\(Click\) \(Here\). 题意就是要求一个图的严格次小生成树.以前被题面吓到了没敢做,写了一下发现并不难. 既然要考虑次小我们就先考虑最小.可以感性理解到一定有一种次小生成树,可以由最小生成树删一条边再加一条边得到.我们枚举加上去的这一条边,加上去以后原\(mst\)会成为一个基环树,想让它次小就在这个环里找一条最长的边(不包含新加进去的)删掉就好.放在树上来讲,就是找到\(u\)到\(v\)路径上的最大值.这样我们就有了非严格次小生成树. 严格要怎么处理?我们需要排除新加

P4180 【模板】严格次小生成树[BJWC2010](严格次小生成树)

题目链接 题意如题 做法 先做一遍最小生成树 枚举添加每一条非树边的情况,每一次构成一棵基环树,在环上找一条最长边(如果等于该非树边就用环上的严格次小边) 倍增LCA,倍增预处理的时候顺便维护严格次大值和最大值(注意细节) (如果是非严格次小生成树则只需要维护最大值即可) 代码 #include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <algo

HDU4081 Qin Shi Huang&#39;s National Road System【Kruska】【次小生成树】

Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3979    Accepted Submission(s): 1372 Problem Description During the Warring States Period of ancient China(4

Ural 1416 Confidential,次小生成树

不严格次小生成树. 注意图可能不连通. #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int maxn = 505; const int INF = 1e7; bool vis[maxn]; int d[maxn]; int pre[maxn]; int Max[maxn][maxn]; int g[

TOJ--1278--最小生成树

今天中午做的 第一次用邻接表去实现... 我就写了下prim的 相比于kruskal 还是更喜欢它多一点... 虽然知道prim+heap优化 可是我写不来..... 对于 heap 虽然觉得它的概念很简单 但实现起来真的好伤啊.. 我想 对于prim的理解应该差不多了 基本上可以直接手码出来了 虽然这个很简单.... 以前原来就有一篇 prim的介绍 那我就懒的写了 直接上代码吧  一般都是用  邻接矩阵实现的.. #include <iostream> #include <cstri