[洛谷P4234] 最小差值生成树

题目类型:(LCT)动态维护最小生成树

传送门:>Here<

题意:求一棵生成树,其最大边权减最小边权最小

解题思路

和魔法森林非常像。先对所有边进行排序,每次加边的时候删除环上的最小边即可

正确性好像很显然,显然由于每一条边一定会被加入,所以最大边权是可以确定的,然后在所有小于等于自己的边权中已经尽量去除了最小的,这是一个贪心

问题在于,由于这里和魔法森林不一样,不要求一个路径上的,而是整颗生成树。因此单单利用(split)来维护好像有点棘手。

我们考虑我们是按从小到大的顺序加边的,而每一次又有环内最小的边。有点像一个队列……我们可以用一个队列来维护所有当前在最小生成树中的边,每一次更新队尾。由于是从小到大的,因此队头就是最大的,队尾就是最小的。由于每一次有可能有最小值出队,所以需要向后维护一下。如果出队的不是最小值,那么根本就不用去管。

至于有哪些边在最小生成树内,用个(bool)的桶来维护一下就好了,非常(eazy)

反思

这题竟然有自环……

另外,在这道题目里,(splay)应该维护最大值。而最大值在打擂的时候要注意儿子的存在问题。或者我们可以将(val[0])设为无限大。

Code

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 50010;
const int MAXM = 200010;
const int MAXS = MAXN + MAXM;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
    if(c == '-') w = -1, c = getchar();
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
struct Edge{
    int x,y,v;
}e[MAXM];
int N,M,ans(INF),MX,cnt;
int val[MAXS],mn[MAXS],q[MAXM],top,head=1;
bool used[MAXM];
struct LinkCutTree{
    int ch[MAXS][2],fa[MAXS];
    bool tag[MAXS];
    inline bool rson(int f, int x){
        return ch[f][1] == x;
    }
    inline bool isroot(int x){
        return ch[fa[x]][rson(fa[x],x)]!=x;
    }
    inline void pushup(int x){
        mn[x] = x;
        if(val[mn[ch[x][0]]] < val[mn[x]] && ch[x][0]) mn[x] = mn[ch[x][0]];
        if(val[mn[ch[x][1]]] < val[mn[x]] && ch[x][1]) mn[x] = mn[ch[x][1]];
    }
    inline void rotate(int x){
        int f = fa[x], gf = fa[f];
        bool p = rson(f,x), q = !p;
        if(!isroot(f)) ch[gf][rson(gf,f)] = x; fa[x] = gf;
        ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
        ch[x][q] = f, fa[f] = x;
        pushup(f), pushup(x);
    }
    void reverse(int x){
        if(!isroot(x)) reverse(fa[x]);
        if(!tag[x]) return;
        tag[x] = 0;
        swap(ch[x][0], ch[x][1]);
        tag[ch[x][0]] ^= 1, tag[ch[x][1]] ^= 1;
    }
    inline void splay(int x){
        for(reverse(x); !isroot(x); rotate(x)){
            if(isroot(fa[x])){ rotate(x); break; }
            if(rson(fa[fa[x]],fa[x]) ^ rson(fa[x],x)) rotate(x); else rotate(fa[x]);
        }
    }
    inline void access(int x){
        for(int y = 0; x; y=x, x = fa[x]) splay(x), ch[x][1] = y, pushup(x);
    }
    inline void mroot(int x){
        access(x);
		splay(x);
		tag[x] ^= 1;
    }
    inline int findroot(int x){
        access(x), splay(x);
        while(ch[x][0]) x = ch[x][0];
        return x;
    }
    inline void split(int x, int y){
        mroot(x);
        access(y);
        splay(y);
    }
    inline void link(int x, int y){
		if(findroot(x) == findroot(y)) return;
        mroot(x);
        fa[x] = y;
    }
    inline void cut(int x, int y){
        split(x,y);
        if(ch[y][0] != x || ch[x][1] != 0) return;
        ch[y][0] = fa[x] = 0;
    }
    inline void debug(){
    	for(int i = 1; i <= M+N; ++i){
    		printf("%d ls=%d rs=%d fa=%d min=%d %d
",i,ch[i][0],ch[i][1],fa[i],val[mn[i]],mn[i]);
		}
	}
}qxz;
inline bool cmp(const Edge& a, const Edge& b){
    return a.v < b.v;
}
int main(){
//	freopen(".in","r",stdin);
    N = read(), M = read();
    for(int i = 1; i <= M; ++i){
        e[i].x = read(), e[i].y = read(), e[i].v = read();
    }
    sort(e+1, e+M+1, cmp);
    for(int i = 1; i <= N; ++i) val[i] = INF;
	for(int i = N+1; i <= N+M; ++i) val[i] = e[i-N].v;
	for(int i = 1; i <= N+M; ++i) mn[i] = i;
    for(int i = 1; i <= M; ++i){
    	if(e[i].x == e[i].y) continue;
        if(qxz.findroot(e[i].x) != qxz.findroot(e[i].y)){
            qxz.link(e[i].x, N+i);
            qxz.link(e[i].y, N+i);
            MX = e[i].v;
            ++cnt;
            used[i] = 1;
            q[++top] = i;
        }
        else{
            qxz.split(e[i].x, e[i].y);
            int temp = mn[e[i].y]-N;
            used[temp] = 0;
            used[i] = 1;
            qxz.cut(e[temp].x, temp+N);
            qxz.cut(e[temp].y, temp+N);
            qxz.link(e[i].x, N+i);
            qxz.link(e[i].y, N+i);
            q[++top] = i;
        }
        if(cnt == N-1){
            qxz.split(1, N);
            while(!used[q[head]]) ++head;
            ans = Min(ans, val[q[top]+N] - val[q[head]+N]);
        }
    }
    printf("%d", ans);
}
原文地址:https://www.cnblogs.com/qixingzhi/p/9740881.html