LCT动态维护最小生成树

本文介绍了一种利用线段树优化的链接切割树(LCT)数据结构,用于解决动态维护树上边权最值及查找最近公共祖先等操作。通过实例展示了如何在O(logn)时间内完成相关操作,适用于解决复杂度要求较高的树形结构问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <bits/stdc++.h>
/*#include <iostream>
#include <climits>
#include <cstring>
#include <algorithm>*/
using namespace  std;
#define  int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
#define fi first
#define se second
#define pb  push_back
#define inf 1ll<<62
#define endl "\n"
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define de_bug(x) cerr << #x << "=" << x << endl
#define all(a) a.begin(),a.end()
#define IOS   std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define  fer(i,a,b)  for(int i=a;i<=b;i++)
#define  der(i,a,b)  for(int i=a;i>=b;i--)
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
int n, m , k;
int  w[N];
struct LCT {

	struct node {
		int s[2], fa, v;
		int id, ma;
		int sum, rev;
#define ls(x) tr[x].s[0]
#define rs(x) tr[x].s[1]
	} tr[N];
	void pushrev(int x) {
		swap(ls(x), rs(x));
		tr[x].rev ^= 1;
	}

	/*void pushup(int x) {
		tr[x].sum = tr[ls(x)].sum ^ tr[x].v ^ tr[rs(x)].sum;
	}*/
	void pushup(int x) {
		tr[x].id = x;
		tr[x].ma = w[x];
		if(tr[tr[x].s[0]].ma > tr[x].ma) {
			tr[x].ma = tr[tr[x].s[0]].ma;
			tr[x].id = tr[tr[x].s[0]].id;
		}
		if(tr[tr[x].s[1]].ma > tr[x].ma) {
			tr[x].ma = tr[tr[x].s[1]].ma;
			tr[x].id = tr[tr[x].s[1]].id;
		}
	}
	void pushdown(int x) {
		if(tr[x].rev) {
			pushrev(ls(x));
			pushrev(rs(x));
			tr[x].rev = 0;
		}
	}
	bool isroot(int x) {
		//判断是不是整颗splay的根节点
		return tr[tr[x].fa].s[0] != x && tr[tr[x].fa].s[1] != x;
	}
	int stk[N];
	int top;
	void rotate(int x) {
		int y = tr[x].fa;
		int z = tr[y].fa;
		int k = rs(y) == x;
		if(!isroot(y))tr[z].s[rs(z) == y] = x;
		tr[x].fa = z;
		tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].fa = y;
		tr[x].s[k ^ 1] = y, tr[y].fa = x;
		//  注意pushup的顺序
		pushup(y);
		pushup(x);
	}
	void splay(int x) {
		//这里如果使用 STL 会  TLE
		int top = 0, r = x;
		stk[ ++ top] = r;
		while (!isroot(r)) stk[ ++ top] = r = tr[r].fa;
		while (top) pushdown(stk[top -- ]);
		while (!isroot(x)) {
			int y = tr[x].fa, z = tr[y].fa;
			if (!isroot(y)) {
				if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
				else rotate(y);
			}
			rotate(x);
		}
	}
	int access(int x) {
		//建立一条从根节点到x节点的 实边  其他边全部变为虚边
		int z = x;
		int y = 0;
		while(x) {
			splay(x);
			tr[x].s[1] = y;
			pushup(x);
			y = x;
			x = tr[x].fa;
		}
		splay(z);
		return y;
	}
	void makeroot(int x) {
		// 将x变为原树的根节点
		access(x);
		pushrev(x);
	}
	int findroot(int x) {
		//找到x所在原树的根节点  并将其旋转到splay的根节点 (防止复杂度退化为o(n))
		access(x);
		while(ls(x))pushdown(x), x = ls(x);
		splay(x);
		return x;
	}
	void split(int x, int y) {
		//给x到y之间建立一个splay 根节点变为y
		makeroot(x);
		access(y);
		splay(y);
	}
	void link(int x, int y) {
		//如果x和y不连通  将x并到y上
		makeroot(x);
		if(findroot(y) != x)tr[x].fa = y;
	}

	void cut(int x, int y) {
		//如果x和y之间存在边 则删除该边
		//将x变为树根 然后 查看y是不是x的后继
		makeroot(x);
		if(findroot(y) == x && tr[y].fa == x && !tr[y].s[0]) {
			tr[x].s[1] = tr[y].fa = 0;
			pushup(x);
		}
	}

	int lca(int root, int x, int y) {
		//  求以root为根的情况下x和y的lca
		makeroot(root);
		access(x);
		return access(y);
	}
	bool check(int x, int y) {
		makeroot(x);
		return findroot(y) != x;
	}
} lct;
int tot;
int idx;
int ans ;
void solve() {
	cin >> n >> m;
	idx = n;
	int ans = 0;
	for(int i = 1; i <= m; i++) {
		int  a, b, c;
		cin >> a >> b >> c;
		w[++idx] = c;
		if(a != b && lct.check(a, b)) {
			ans += c;
			lct.link(a, idx);
			lct.link(idx, b);
			tot++;
		} else {
			lct.split(a, b);
			int tmp = lct.tr[b].id;
			int val = lct.tr[tmp].ma;
			if(val <= c)continue;
			ans -= val;
			ans += c;
			lct.splay(tmp);
			lct.tr[lct.tr[tmp].s[0]].fa = 0;
			lct.tr[lct.tr[tmp].s[1]].fa = 0;
			lct.link(a, idx);
			lct.link(idx, b);
		}
	}
	if(tot != n - 1) cout << "orz" << endl;
	else cout << ans << endl;
}
signed main() {
	IOS;
	int _ = 1;
	//cin>>_;
	while( _-- )
		solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值