【BZOJ3669】【UOJ3】【NOI2014】魔法森林

【题目链接】

【思路要点】

  • 对边的权值\(A\)排序,依次加入图中,用LinkCutTree维护权值\(B\)的最小生成树。
  • 时间复杂度\(O(MLogN)\)。

【代码】

#include<bits/stdc++.h>
using namespace std; 
#define MAXN	50005
#define MAXM	100005
#define INF	1e9
struct querys {int x, y, a, b; }; 
struct info {int value, pos; }; 
struct LCT {
	struct Node {
		bool rev; 
		int num, value, maxnum, pos; 
		int father, child[2], up; 
	}; 
	Node a[MAXN+MAXM]; 
	int home[MAXN+MAXM], size; 
	private:
	void pushdown(int x) {
		if (!a[x].rev) return; 
		a[x].rev = false; 
		swap(a[x].child[0], a[x].child[1]); 
		if (a[x].child[0]) a[a[x].child[0]].rev ^= true; 
		if (a[x].child[1]) a[a[x].child[1]].rev ^= true; 
	}
	void update(int x) {
		a[x].maxnum = a[x].value; 
		a[x].pos = x; 
		if (a[x].child[0] && a[a[x].child[0]].maxnum>a[x].maxnum) {
			a[x].maxnum = a[a[x].child[0]].maxnum; 
			a[x].pos = a[a[x].child[0]].pos; 
		}
		if (a[x].child[1] && a[a[x].child[1]].maxnum>a[x].maxnum) {
			a[x].maxnum = a[a[x].child[1]].maxnum; 
			a[x].pos = a[a[x].child[1]].pos; 
		}
	}
	bool get(int x) {
		pushdown(a[x].father); 
		return a[a[x].father].child[1] == x; 
	}
	void rotate(int x) {
		int f = a[x].father, g = a[f].father; 
		pushdown(f); pushdown(x); 
		bool tmp = get(x); 
		a[x].up = a[f].up; 
		a[f].up = 0; 
		a[f].child[tmp] = a[x].child[tmp^1]; 
		a[a[x].child[tmp^1]].father = f; 
		a[f].father = x; 
		a[x].child[tmp^1] = f; 
		a[x].father = g; 
		if (g) a[g].child[a[g].child[1] == f] = x; 
		update(f); update(x); 
	}
	void splay(int x) {
		pushdown(x); 
		for (int f = a[x].father; (f = a[x].father); rotate(x))
			if (a[f].father) rotate(get(x) == get(f)?f:x); 
	}
	void access(int x) {
		splay(x); 
		int tmp = a[x].child[1]; 
		a[tmp].father = 0; 
		a[tmp].up = x; 
		a[x].child[1] = 0; 
		update(x); 
		for (int f = a[x].up; f; f = a[x].up) {
			splay(f); 
			tmp = a[f].child[1]; 
			a[tmp].father = 0; 
			a[tmp].up = f; 
			a[f].child[1] = x; 
			a[x].father = f; 
			a[x].up = 0; 
			update(f); 
			x = f; 
		}
	}
	void reverse(int x) {
		access(x); 
		splay(x); 
		a[x].rev ^= true; 
	}
	int find_root(int x) {
		access(x); 
		splay(x); 
		int now = x; 
		while (a[now].child[0]) {
			now = a[now].child[0]; 
			pushdown(now); 
		}
		splay(now);
		return now; 
	}
	void link(int x, int y) {
		reverse(x); 
		splay(x); 
		access(y); 
		splay(y); 
		a[y].child[1] = x; 
		a[x].father = y; 
		update(y); 
	}
	void cut(int x, int y) {
		reverse(x); 
		access(y); 
		splay(y); 
		a[x].father = 0; 
		a[y].child[0] = 0; 
		update(y); 
	}
	int new_node(int num, int value) {
		size++; 
		a[size].num = num; 
		a[size].value = value; 
		a[size].maxnum = value; 
		a[size].pos = size; 
		a[size].rev = false; 
		a[size].father = 0; 
		a[size].child[0] = 0; 
		a[size].child[1] = 0; 
		a[size].up = 0; 
		return size; 
	}
	public:
	void make_tree(int num, int value) {
		home[num] = new_node(num, value); 
	}
	void insert(int x, int y) {
		link(home[x], home[y]); 
	}
	void del(int x, int y) {
		cut(home[x], home[y]); 
	}
	int get_root(int x) {
		return a[find_root(home[x])].num; 
	}
	info query(int x, int y) {
		x = home[x]; y = home[y]; 
		reverse(x); 
		access(y); 
		splay(y); 
		info ans = (info){a[y].maxnum, a[a[y].pos].num}; 
		return ans; 
	}
}; 
querys q[MAXM]; 
LCT T; 
bool cmp(querys x, querys y) {
	return x.a<y.a || (x.a == y.a && x.b<y.b); 
}
int main() {
	int n, m; 
	scanf("%d%d", &n, &m); 
	for (int i = 1; i <= m; i++)
		scanf("%d%d%d%d", &q[i].x, &q[i].y, &q[i].a, &q[i].b); 
	sort(q+1, q+1+m, cmp); 
	int ans = INF; 
	for (int i = 1; i <= n; i++)
		T.make_tree(i, 0); 
	for (int i = 1; i <= m; i++) {
		T.make_tree(i+n, q[i].b); 
		if (T.get_root(q[i].x) != T.get_root(q[i].y)) {
			T.insert(i+n, q[i].x); 
			T.insert(i+n, q[i].y); 
		}
		else {
			info tmp = T.query(q[i].x, q[i].y); 
			if (tmp.value>q[i].b) {
				T.del(tmp.pos, q[tmp.pos-n].x); 
				T.del(tmp.pos, q[tmp.pos-n].y); 
				T.insert(i+n, q[i].x); 
				T.insert(i+n, q[i].y); 
			}
		}
		if (T.get_root(1) == T.get_root(n))
			ans = min(ans, q[i].a+T.query(1, n).value);
	}
	if (ans == INF) printf("-1\n"); 
	else printf("%d\n", ans); 
	return 0; 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值