• P4234 最小差值生成树 LCT维护边权


    (color{#0066ff}{ 题目描述 })

    给定一个标号为从 (1)(n) 的、有 (m) 条边的无向图,求边权最大值与最小值的差值最小的生成树。

    (color{#0066ff}{输入格式})

    第一行两个数 (n, m),表示图的点和边的数量。

    第二行起 mm 行,每行形如 u_i, v_i, w_iui,vi,wi,代表 u_iui 到 v_ivi 间有一条长为 w_iwi 的无向边。

    (color{#0066ff}{输出格式})

    输出一行一个整数,代表你的答案。

    数据保证存在至少一棵生成树。

    (color{#0066ff}{输入样例})

    4 6 
    1 2 10 
    1 3 100 
    1 4 90 
    2 3 20 
    2 4 80 
    3 4 40 
    

    (color{#0066ff}{输出样例})

    20
    

    (color{#0066ff}{数据范围与提示})

    对于 30% 的数据,满足 (1 leq n leq 100, 1 leq m leq 1000)

    对于 97% 的数据,满足 (1 leq n leq 500, 1 leq m leq 100000)

    对于 100% 的数据,满足 (1 leq n leq 50000, 1 leq m leq 200000, 1 leq w_i leq 10000)

    (color{#0066ff}{题解})

    把所有边从大到小排个序,依次加入LCT,LCT上维护边权(拆成点)

    如果不成环,直接加入边,用一个变量维护LCT上的边数

    如果成环了,那么显然把最大的换下来更优

    用multiset维护最大最小值,每次只要是生成树就更新

    #include<bits/stdc++.h>
    #define LL long long
    LL in() {
    	char ch; LL x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int maxn = 5e5 + 10;
    const int inf = 0x7fffffff;
    struct node {
    	node *ch[2], *fa;
    	int max, val, rev;
    	node(int max = 0, int val = 0, int rev = 0): max(max), val(val), rev(rev) { ch[0] = ch[1] = fa = NULL; }
    	void trn() { std::swap(ch[0], ch[1]), rev ^= 1; }
    	void dwn() {
    		if(!rev) return;
    		if(ch[0]) ch[0]->trn();
    		if(ch[1]) ch[1]->trn();
    		rev = 0;
    	}
    	void upd() {
    		max = val;
    		if(ch[0]) max = std::max(max, ch[0]->max);
    		if(ch[1]) max = std::max(max, ch[1]->max);
    	}
    	bool ntr() { return fa && (fa->ch[0] == this || fa->ch[1] == this); }
    	bool isr() { return fa->ch[1] == this; }
    	void clr() { 
    		if(ch[0]) ch[0]->fa = NULL;
    		if(ch[1]) ch[1]->fa = NULL;
    		ch[0] = ch[1] = NULL;
    	}
    }pool[maxn];
    struct EDGE {
    	int x, y, z;
    	friend bool operator < (const EDGE &a, const EDGE &b) { return a.z > b.z; }
    }e[maxn];
    int ans = inf, num;
    std::multiset<int> s;
    int n, m;
    void rot(node *x) {
    	node *y = x->fa, *z = y->fa;
    	bool k = x->isr(); node *w = x->ch[!k];
    	if(y->ntr()) z->ch[y->isr()] = x;
    	(x->ch[!k] = y)->ch[k] = w;
    	(y->fa = x)->fa = z;
    	if(w) w->fa = y;
    	y->upd(), x->upd();
    }
    void splay(node *o) {
    	static node *st[maxn];
    	int top;
    	st[top = 1] = o;
    	while(st[top]->ntr()) st[top + 1] = st[top]->fa, top++;
    	while(top) st[top--]->dwn();
    	while(o->ntr()) {
    		if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
    		rot(o);
    	}
    }
    node *find(node *o) {
    	while(o->dwn(), o->max != o->val) {
    		if(o->ch[0] && o->ch[0]->max == o->max) o = o->ch[0];
    		else o = o->ch[1];
    	}
    	return o;
    }
    void access(node *x) {
    	for(node *y = NULL; x; x = (y = x)->fa)
    		splay(x), x->ch[1] = y, x->upd();
    }
    void makeroot(node *x) { access(x), splay(x), x->trn(); }
    node *findroot(node *o) {
    	access(o), splay(o);
    	while(o->dwn(), o->ch[0]) o = o->ch[0];
    	return o;
    }
    void link(node *x, node *y) { makeroot(x), x->fa = y; }
    void add(node *x, node *y, node *o) {
    	if(findroot(x) == findroot(y)) {
    		makeroot(x), access(y), splay(y);
    		num--;
    	    node *p = find(y);
           	s.erase(s.find(p->max));
    		splay(p);
    		p->clr(), p->upd();
    	}
    	num++;
    	s.insert(o->val);
    	if(num == n - 1 && num) {
        	std::multiset<int>::iterator be = s.begin(), ed = s.end();
    		ed--;
    		ans = std::min(ans, *ed - *be);
    	}
    	link(x, o), link(y, o);
    }
    int main() {
    	n = in(), m = in();
    	for(int i = 1; i <= m; i++) e[i].x = in(), e[i].y = in(), e[i].z = in();
    	std::sort(e + 1, e + m + 1);
    	for(int i = 1; i <= m; i++) {
    		if(e[i].x == e[i].y) continue;
    		pool[n + i].val = e[i].z;
    		pool[n + i].upd();
    		add(pool + e[i].x, pool + e[i].y, pool + n + i);
    	}
    	printf("%d", ans);
    	return 0;
    }
    
  • 相关阅读:
    韦德螺旋: 这真是一个螺旋吗?
    山上你能看到什么动物?
    你能够30秒内一字不差的念完它吗? 注意, 是读“颜色”, 不是让你识字.
    路透斯沃德的不可能的三角形
    换个角度, 青蛙也许就是白马王子
    这是一张很有趣的图片, 通常女性会先看到月亮, 男性会先看到人脸. 如果相反, 表示你体内的异性荷尔蒙偏高哦!
    亲吻的情侣幻觉: 这幅虚幻的亲吻由美国艺术家杰里•唐恩创作.
    PostgreSQL的 initdb 源代码分析之七
    PostgreSQL的initdb 源代码分析之六
    PostgreSQL的initdb 源代码分析之五
  • 原文地址:https://www.cnblogs.com/olinr/p/10416970.html
Copyright © 2020-2023  润新知