ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BZOJ 1977 : [BJWC2010]严格次小生成树 倍增 Kruskal

2019-10-13 16:02:59  阅读:376  来源: 互联网

标签:ch return 1977 Kruskal BJWC2010 int read inline 生成


title

BZOJ 1977

LUOGU 4180

Description

小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。

正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。

小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 \(E_M\),严格次小生成树选择的边集是 \(E_S\),那么需要满足:( \(value(e)\) 表示边 \(e\) 的权值)
\[ \sum_{e\in E_M}value(e)<\sum_{e\in E_S}value(e) \]

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

Input Format

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

Output Format

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

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\leqslant 2 000,M\leqslant 3 000\);

\(80\%\) 的数据 \(N\leqslant 50 000,M\leqslant 100 000\);

\(100\%\) 的数据 \(N\leqslant 100 000,M\leqslant 300 000\) ,边权值非负且不超过 \(10^9\) 。

analysis

以前写的时候看了半天题解,调了很长时间,现在就有些不能理解当时怎么那么难,哎。

题目要求严格次小生成树,何为严格?大概是如此:

严格次小生成树和最小生成树之间在没有边权相同的边情况下有且仅有一条边不同。

所以求出非严格次小生成树便是十分简单的事。

那怎么求严格次小?

  • 求出 \(MST\) ,顺便标出树边与非树边;

  • 考虑加入一条非树边 \((x,y,z)\) ,那么原生成树 \(x,y,lca(x,y)\) 便会出现一个环。那么便要删掉一条边,使得留下的边权比原来的边权大:
    • 比较明显地,如果环上最长边删掉后,生成树权值没有变化,那就删除次长边。
    • 不然删除最大边。

假如可以快速地求出生成树中一条路径 \(x,y,lca(x,y)\) 的最长边和次长边,那么扫一遍就可以知道答案了。

怎么办呢?

倍增即可,毕竟还需要求 \(lca\) ,那么顺便求出从某个点 \(x\) 上跳 \(2^i\) 步经过的边权的最大值和次大值即可。

至此,整道题基本结束。

code

#include <bits/stdc++.h>

#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)

#define Grt ch = getchar()
#define DeBug(x) std::cout << #x << " = " << x << std::endl

typedef long long ll;
const int MaxN = 1e5 + 10, MaxM = 3e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3fll;

namespace IO
{
    char buf[1<<15], *fs, *ft;
    inline char getc() { return ft == fs && (ft = (fs = buf) + fread(buf, 1, 1 << 15, stdin), ft == fs) ? 0 : *fs ++; }
    template <typename T> inline void read(T &x)
    {
        x = 0;
        T f = 1, Grt;
        while (!isdigit(ch) && ch ^ '-') Grt;
        if (ch == '-') f = -1, Grt;
        while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), Grt;
        x *= f;
    }

    template <typename T, typename... Args>
    inline void read(T &x, Args &...args) { read(x); read(args...); }

    char Out[1<<24], *fe = Out;
    inline void flush() { fwrite(Out, 1, fe - Out, stdout); fe = Out; }
    template <typename T> inline void write(T x, char str)
    {
        if (!x) *fe++ = 48;
        if (x < 0) *fe++ = '-', x = -x;
        T num = 0, ch[20];
        while (x) ch[++ num] = x % 10 + 48, x /= 10;
        while (num) *fe++ = ch[num --];
        *fe++ = str;
    }
}

using IO::read;
using IO::write;

template <typename T> inline bool chkMin(T &a, const T &b) { return a > b ? (a = b, true) : false; }
template <typename T> inline bool chkMax(T &a, const T &b) { return a < b ? (a = b, true) : false; }
template <typename T> inline T min(T a, T b) { return a < b ? a : b; }
template <typename T> inline T max(T a, T b) { return a > b ? a : b; }

struct Orz
{
    int x, y, z;
    bool flag;
    inline bool operator < (const Orz &a) const
    {
        return z < a.z;
    }
} e[MaxM];

int ver[MaxM << 1], edge[MaxM << 1], Next[MaxM << 1], head[MaxN], len;
inline void add(int x, int y, int z)
{
    ver[++ len] = y, edge[len] = z, Next[len] = head[x], head[x] = len;
}

namespace lca
{
    int f[MaxN][21], dep[MaxN];
    ll Max[MaxN][21], Min[MaxN][21];//最大,次大
    inline void dfs(int x)
    {
        for (int i = 1; i <= 20; ++ i)
        {
            f[x][i] = f[f[x][i - 1]][i - 1];
            Max[x][i] = max(Max[x][i - 1], Max[f[x][i - 1]][i - 1]);
            Min[x][i] = max(Min[x][i - 1], Min[f[x][i - 1]][i - 1]);

            if (Max[x][i - 1] ^ Max[f[x][i - 1]][i - 1]) chkMax(Min[x][i], min(Max[x][i - 1], Max[f[x][i - 1]][i - 1]));//如果不相同,次大值需要更新
        }
        for (int i = head[x]; i; i = Next[i])
        {
            int y = ver[i];
            if (y == f[x][0]) continue;
            f[y][0] = x;
            dep[y] = dep[x] + 1;
            Max[y][0] = edge[i];//与父亲的最大权值即他们之间的边权,所以是 Max[y][0]
            Min[y][0] = -inf;//与父亲的次大权值未确定,赋值为 -inf
            dfs(y);
        }
    }

    inline int LCA(int x, int y)
    {
        if (dep[x] > dep[y]) std::swap(x, y);
        for (int i = 20; i >= 0; -- i)
            if (dep[y] - (1 << i) >= dep[x]) y = f[y][i];
        if (x == y) return x;
        for (int i = 20; i >= 0; -- i)
            if (f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }

    inline ll jump(int x, int y, ll d)
    {
        ll ans = -inf;
        for (int i = 20; i >= 0; -- i) if (dep[f[x][i]] >= dep[y])
        {
            if (d ^ Max[x][i]) chkMax(ans, Max[x][i]);
            else chkMax(ans, Min[x][i]);
            x = f[x][i];
        }
        return ans;
    }
}

using lca::dfs;
using lca::LCA;
using lca::jump;

int fa[MaxN];
inline int get(int x)
{
    return fa[x] == x ? x : fa[x] = get(fa[x]); 
}

int main()
{
    int n, m; read(n, m);
    for (int i = 1; i <= m; ++ i) read(e[i].x, e[i].y, e[i].z);
    std::sort(e + 1, e + m + 1);
    ll sum = 0;
    int tot = 0;
    for (int i = 1; i <= n; ++ i) fa[i] = i;
    for (int i = 1; i <= m; ++ i)//Kruskal 求 MST
    {
        int x = get(e[i].x), y = get(e[i].y);
        if (x == y) continue;
        add(e[i].x, e[i].y, e[i].z), add(e[i].y, e[i].x, e[i].z);
        e[i].flag = 1;
        fa[x] = y;
        sum += e[i].z;
        if (++ tot == n - 1) break;
    }
    lca::Min[1][0] = -inf;
    dfs(1);
    ll ans = inf;
    for (int i = 1; i <= m; ++ i)
    {
        int x = e[i].x, y = e[i].y, z = e[i].z;
        if (!e[i].flag)
        {
            int l = LCA(e[i].x, e[i].y);
            ll t1 = jump(x, l, z), t2 = jump(y, l, z);
            chkMin(ans, sum - max(t1, t2) + z);
        }
    }
    write(ans, '\n');
    IO::flush();
    return 0;
}

标签:ch,return,1977,Kruskal,BJWC2010,int,read,inline,生成
来源: https://www.cnblogs.com/G-hsm/p/BZOJ1977.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有