其他分享
首页 > 其他分享> > BZOJ 1977 : [BJWC2010]严格次小生成树 倍增 Kruskal

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

作者:互联网

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

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

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

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

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

那怎么求严格次小?

假如可以快速地求出生成树中一条路径 \(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