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

题目链接\(Click\) \(Here\)

题意就是要求一个图的严格次小生成树。以前被题面吓到了没敢做,写了一下发现并不难。

既然要考虑次小我们就先考虑最小。可以感性理解到一定有一种次小生成树,可以由最小生成树删一条边再加一条边得到。我们枚举加上去的这一条边,加上去以后原\(mst\)会成为一个基环树,想让它次小就在这个环里找一条最长的边(不包含新加进去的)删掉就好。放在树上来讲,就是找到\(u\)到\(v\)路径上的最大值。这样我们就有了非严格次小生成树。

严格要怎么处理?我们需要排除新加上的边和\(u\)到\(v\)路径最长边相等的情况。仔细思考会发现可以再倍增维护一个次大长度,如果最大长度严格小于新加上边的长度就选用最大,否则就用次大。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 100000 + 5;
const int M = 300000 + 5;
const int INF = 0x7fffffff;
const LL INFF = 0x7fffffffffffffff;

struct Graph {

    int cnt, head[N];

    struct Edge {int nxt, to, w;}e[N << 1];

    void clear () {
        cnt = -1;
        memset (head, -1, sizeof (head));
    }

    void add_len (int u, int v, int w) {
        e[++cnt] = (Edge) {head[u], v, w}; head[u] = cnt;
        e[++cnt] = (Edge) {head[v], u, w}; head[v] = cnt;
    }

    int deep[N], pre[N][20], w1st[N][20], w2nd[N][20];

    void dfs (int u, int fa) {
        deep[u] = deep[fa] + 1;
        for (int i = head[u]; ~i; i = e[i].nxt) {
            int v = e[i].to;
            if (v != fa) {
                dfs (v, u);
                pre[v][0] = u;
                w2nd[v][0] = -INF;
                w1st[v][0] = e[i].w;
            }
        }
    }

    void build (int u) {
        for (int i = 1; (1 << i) <= deep[u]; ++i) {
            pre[u][i] = pre[pre[u][i - 1]][i - 1];
            w1st[u][i] = max (w1st[u][i - 1], w1st[pre[u][i - 1]][i - 1]);
            w2nd[u][i] = max (w2nd[u][i - 1], w2nd[pre[u][i - 1]][i - 1]);
            if (w1st[u][i - 1] < w1st[pre[u][i - 1]][i - 1]) {
                w2nd[u][i] = max (w2nd[u][i], w1st[u][i - 1]);
            }
            if (w1st[u][i - 1] > w1st[pre[u][i - 1]][i - 1]) {
                w2nd[u][i] = max (w2nd[u][i], w1st[pre[u][i - 1]][i - 1]);
            }
        }
        for (int i = head[u]; ~i; i = e[i].nxt) {
            if (e[i].to != pre[u][0]) build (e[i].to);
        }
    }

    int lca (int u, int v) {
        if (deep[u] < deep[v]) swap (u, v);
        for (int i = 19; i >= 0; --i) {
            if (deep[u] - deep[v] >= (1 << i)) {
                u = pre[u][i];
            }
        }
        if (u == v) return u;
        for (int i = 19; i >= 0; --i) {
            if (pre[u][i] != pre[v][i]) {
                u = pre[u][i];
                v = pre[v][i];
            }
        }
        return pre[u][0];
    }

    int query (int u, int v, int w) {
        //求u到v之间严格小于w的最大的值
        int ans = -INF;
        for (int i = 19; i >= 0; --i) {
            if (deep[v] - deep[u] >= (1 << i)) {
                if (w1st[v][i] < w) {
                    ans = max (ans, w1st[v][i]);
                } else {
                    ans = max (ans, w2nd[v][i]);
                }
                v = pre[v][i];
            }
        }
        return ans;
    }

}G; 

struct Len {
    int u, v, w;

    bool operator < (Len rhs) const {
        return w < rhs.w;
    }

    Len () {}
    Len (int u, int v, int w) : u(u), v(v), w(w) {}
}arr[M];

int n, m, Set[N], in_mst[M];

int find (int x) {
    return x == Set[x] ? x : (Set[x] = find (Set[x]));
}

int main () {
    G.clear ();
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        static int u, v, w;
        cin >> u >> v >> w;
        arr[i] = Len (u, v, w);
    }
    sort (arr, arr + m);
    for (int i = 0; i <= n; ++i) Set[i] = i;
    LL mstw = 0;
    for (int i = 0; i < m; ++i) {
        int u = arr[i].u;
        int v = arr[i].v;
        int w = arr[i].w;
        if (find (u) != find (v)) {
            mstw += w;
            in_mst[i] = true;
            G.add_len (u, v, w);
            Set[find (u)] = find (v);
        }
    }
    G.w1st[1][0] = -INF;
    G.dfs (1, 0);
    G.build (1);
    LL ans = INFF;
    for (int i = 0; i < m; ++i) {
        if (!in_mst[i]) {
            int u = arr[i].u;
            int v = arr[i].v;
            int w = arr[i].w;
            int _lca = G.lca (u, v);
            int maxu = G.query (_lca, u, w);
            int maxv = G.query (_lca, v, w);
            ans = min (ans, mstw + w - max (maxu, maxv));
        }
    }
    cout << ans << endl;
}

原文地址:https://www.cnblogs.com/maomao9173/p/10780180.html

时间: 2024-10-10 01:04:25

P4180 严格次小生成树[BJWC2010] Kruskal,倍增的相关文章

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

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

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

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

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

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

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

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

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

bzoj 1977 洛谷P4180 严格次小生成树

Description: 给定一张N个节点M条边的无向图,求该图的严格次小生成树.设最小生成树边权之和为sum,那么严格次小生成树就是边权之和大于sum的最小的一个 Input: 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z. Output: 包含一行,仅一个数,表示严格次小生成树的边权和.(数据保证必定存在严格次小生成树) 思路:先求出原图的最小生成树,然后继续从小到大枚举边(x,y),对于x,y用倍

poj1679——The Unique MST(次小生成树,Kruskal)

Description Given a connected undirected graph, tell if its minimum spanning tree is unique. Definition 1 (Spanning Tree): Consider a connected, undirected graph G = (V, E). A spanning tree of G is a subgraph of G, say T = (V', E'), with the followin

洛谷.4180.[模板]次小生成树Tree(Kruskal LCA 倍增)

构建完MST后,枚举非树边(u,v,w),在树上u->v的路径中找一条权值最大的边(权为maxn),替换掉它这样在 w=maxn 时显然不能满足严格次小.但是这个w可以替换掉树上严格小于maxn的次大边用倍增维护MST上路径的最大值.次大值,每条非树边的查询复杂度就为O(logn) ps:1.倍增更新次大值时,未必是从最大值转移,要先赋值较大的次大值,再与较小的那个最大值比较.2.maxn!=w时,是可以从maxn更新的(不能更新就是上面情况啊)倍增处理部分我还是在dfs里写吧 md改了一晚上

POJ1679 The Unique MST 题解 次小生成树 题解 Kruskal+暴力LCA解法(因为倍增不会写)

题目链接:http://poj.org/problem?id=1679 题目大意: 给你一个简单连通图,判断他的最小生成树是否唯一. 解题思路: 首先(我这里用Kruskal算法)求出它的最小生成树(以下简称MST)以及对应的边,然后构造出这棵MST. 然后我们枚举图上每一条不在此MST上的边,假设这条边的两个端点是 \(u\) 和 \(v\),边权为 \(w\) ,求MST上 \(u\) 到 \(v\) 的树链上的最大边的值是否等于 \(w\),如果等于 \(w\) 就说明 MST 不唯一.