bzoj 3669 [Noi2014]魔法森林 LCT

题面

题目传送门

解法

维护双关键字好像不太可做吧

所以我们先按照\(a\)从小到大排序,然后逐一检查\(b\)

如果构成环,那么把环上最大的\(b\)删掉

只要出现1和\(n\)连通的时候就更新答案

时间复杂度:\(O((n+m)\ log\ n)\)

代码

#include <bits/stdc++.h>
#define N 150010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
	int x, y, p, q;
	bool operator < (const Edge &a) const {
		return p < a.p;
	}
} a[N];
namespace LCT {
	struct Node {
		int fa, num, rev, val, pathfa, child[2];
	} t[N];
	int son(int x, int y) {return t[x].child[1] == y;}
	int val(int x) {if (!x) return 0; return t[x].val;}
	int num(int x) {if (!x) return 0; return t[x].num;}
	void rev(int x) {if (!x) return; t[x].rev ^= 1;}
	void update(int x) {
		if (!x) return; t[x].num = x;
		if (val(t[x].num) < val(num(t[x].child[0]))) t[x].num = num(t[x].child[0]);
		if (val(t[x].num) < val(num(t[x].child[1]))) t[x].num = num(t[x].child[1]);
	}
	void pushdown(int x) {
		if (!x) return;
		int k = t[x].rev;
		if (k) {
			rev(x), swap(t[x].child[0], t[x].child[1]);
			rev(t[x].child[0]), rev(t[x].child[1]);
		}
	}
	void Connect(int x, int y, int k) {
		if (x) t[x].child[k] = y;
		if (y) t[y].fa = x; update(x);
	}
	void Rotate(int x) {
		int y = t[x].fa, z = t[y].fa;
		pushdown(y), pushdown(x);
		swap(t[x].pathfa, t[y].pathfa);
		int a = son(y, x), b = !a;
		Connect(z, x, son(z, y));
		Connect(y, t[x].child[b], a);
		Connect(x, y, b);
		update(y), update(x);
	}
	void Splay(int x) {
		while (t[x].fa) {
			int y = t[x].fa, z = t[y].fa;
			if (z) {
				pushdown(z), pushdown(y);
				(son(z, y) ^ son(y, x)) ? Rotate(x) : Rotate(y);
			}
			Rotate(x);
		}
	}
	void expose(int x) {
		Splay(x), pushdown(x);
		int y = t[x].child[1];
		t[y].fa = 0, t[y].pathfa = x;
		t[x].child[1] = 0, update(x);
	}
	void access(int x) {
		for (expose(x); t[x].pathfa; Splay(x)) {
			expose(t[x].pathfa);
			Connect(t[x].pathfa, x, 1);
			t[x].pathfa = 0;
		}
	}
	void evert(int x) {access(x), Splay(x), rev(x);}
	void link(int x, int y) {evert(y), t[y].pathfa = x;}
	void cut(int x, int y) {
		evert(x), access(y);
		Splay(y), pushdown(y);
		t[t[y].child[0]].fa = 0;
		t[y].child[0] = 0, update(y);
	}
	int query(int x, int y) {
		evert(x); access(y); Splay(y);
		return t[y].num;
	}	
}
int p[N];
int Find(int x) {
	if (p[x] == x) return x;
	return p[x] = Find(p[x]);
}
int main() {
	using namespace LCT;
	int n, m; read(n), read(m);
	for (int i = 1; i <= n; i++) t[i].num = i;
	for (int i = 1; i <= m; i++)
		read(a[i].x), read(a[i].y), read(a[i].p), read(a[i].q);
	sort(a + 1, a + m + 1); int ans = INT_MAX;
	for (int i = n + 1; i <= n + m; i++) t[i].val = a[i - n].q;
	for (int i = 1; i <= n + m; i++) t[i].num = i;
	for (int i = 1; i <= n; i++) p[i] = i;
	for (int i = 1; i <= m; i++) {
		int tx = Find(a[i].x), ty = Find(a[i].y);
		if (tx != ty) {
			p[tx] = ty;
			link(a[i].x, i + n), link(a[i].y, i + n);
		} else {
			int tmp = query(a[i].x, a[i].y);
			if (val(tmp) > a[i].q) {
				int pos = tmp - n, x = a[pos].x, y = a[pos].y;
				cut(x, tmp), cut(y, tmp);
				link(a[i].x, i + n), link(a[i].y, i + n);
			}
		}
		if (Find(1) == Find(n)) {
			int tmp = query(1, n);
			chkmin(ans, a[i].p + val(tmp));
		}
	}
	if (ans != INT_MAX) cout << ans << "\n";
		else cout << "-1\n";
	return 0;
}
posted @ 2018-08-14 22:13  谜のNOIP  阅读(141)  评论(0编辑  收藏  举报