[Luogu] 最小差值生成树

https://www.luogu.org/recordnew/show/6125570

思路就是巧妙的枚举所有的生成树,取最优值
首先按照边权排序
找出第一颗最小生成树(l, r),其中l表示最小边的编号,r表示最大边的编号
然后从r+1号边开始倒序枚举各边,求出第二颗最小生成树(当然也可能不存在)(l_2, r_2), r_2 = r + 1.
这样的话就省去了多条最小边的枚举
比如若从2 -- l_2-1中任选一条边作为最小边开始查询最大生成树,那么最大边一定为r_2
这样的话差值不会比(l_2, r_2)这颗生成树更优,所以就简化了算法
然后从将l_2+1号边作为最小边开始枚举,依次进行下去

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 5e4 + 10;

#define gc getchar()

int fa[N];
struct Node{int u, v, w;} G[N << 2];
int n, m, Answer = 999999999;

inline int read() {
    int x = 0; char c = gc;
    while(c < '0' || c > '9') c = gc;
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
    return x;
}

inline bool cmp(Node a, Node b) {return a.w < b.w;}
int get_fa(int x) {return fa[x] == x ? x : fa[x] = get_fa(fa[x]);}
void Printf() {cout << Answer; exit(0);}

void dfs(int start, int how) {
    for(int i = 1; i <= n; i ++) fa[i] = i;
    int tot = 0, Maxn = 0, Minn = 0, Nxt;
    if(how % 2) {//cong xiao dao da
        for(int i = start; i <= m; i ++) {
            int fa_u = get_fa(G[i].u), fa_v = get_fa(G[i].v);
            if(fa_u != fa_v) {
                fa[fa_u] = fa_v;
                tot ++;
                if(tot == n - 1) {Maxn = G[i].w; Nxt = i; break;}
            }
        }
        if(!Maxn) Printf();
        Answer = min(Answer, Maxn - G[start].w);
    } else {
        for(int i = start; i >= 1; i --) {
            int fa_u = get_fa(G[i].u), fa_v = get_fa(G[i].v);
            if(fa_u != fa_v) {
                fa[fa_u] = fa_v;
                tot ++;
                if(tot == n - 1) {Minn = G[i].w; Nxt = i; break;
                }
            }
        }
        if(!Minn) Printf();
        Answer = min(Answer, G[start].w - Minn);
    }
    dfs(Nxt + 1, how + 1);
}

int main() {
    n = read();
    m = read();
    for(int i = 1; i <= m; i ++) G[i].u = read(), G[i].v = read(), G[i].w = read();
    sort(G + 1, G + m + 1, cmp);
    dfs(1, 1);
    return 0;
}
原文地址:https://www.cnblogs.com/shandongs1/p/8480832.html