Luogu2387 [NOI2014]魔法森林

题目蓝链

Description

给你一个图,图上每条边都有两个权值\(a, b\)。经过一条边需要自身的属性\(x, y\)满足\(a \leq x, b \leq y\)。你现在要从\(1\)号点走到\(n\)号点,问你最小需要的自身属性\(x, y\)的和

Solution

我们可以考虑把边按\(a_i\)先进行排序,然后依次加入到图中。因为当边的\(a_i\)的最大值一定时,我们只需要最小化\(b_i\)即可。所以我们直接把\(b_i\)看成权值,然后用LCT去维护最小生成树就可以了

在用LCT维护的时候,我们可以把每条边都对应建一个点,然后这个点的点权即为对应边的边权,其它点的点权为\(0\)。我们每添加一条边,需要先判断是否已经联通。若已联通,并且链上的最大值比当前添加的边的边权要大,则用当前边去替换

Code

#include <bits/stdc++.h>

using namespace std;

#define fst first
#define snd second
#define mp make_pair
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }

inline int read() {
	int sum = 0, fg = 1; char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
	return fg * sum;
}

const int maxn = 5e4 + 10;
const int maxm = 1e5 + 10;
const int inf = 5e4;

int n, m;

struct edge {
	int x, y, a, b;
	void input() { x = read(), y = read(), a = read(), b = read(); }
	bool operator < (const edge &t) const { return a < t.a; }
}e[maxm];

namespace LCT {

	struct node {
		int son[2], f;
		pii v, s; bool rev;
	}A[maxn + maxm];
#define ls(x) A[x].son[0]
#define rs(x) A[x].son[1]
#define fa(x) A[x].f

	void push_up(int x) { A[x].s = max(A[x].v, max(A[ls(x)].s, A[rs(x)].s)); }
	void push_down(int x) {
		if (A[x].rev) {
			swap(ls(x), rs(x));
			A[ls(x)].rev ^= 1, A[rs(x)].rev ^= 1, A[x].rev = 0;
		}
	}

	bool chkrt(int x) { return x != ls(fa(x)) && x != rs(fa(x)); }
	bool chk(int x) { return x == rs(fa(x)); }
	void link(int x, int y, int f) { fa(x) = y, A[y].son[f] = x; }
	void rotate(int x) {
		int f = fa(x), dx = chk(x), df = chk(f);
		link(A[x].son[dx ^ 1], f, dx);
		if (!chkrt(f)) link(x, fa(f), df); else fa(x) = fa(f);
		link(f, x, dx ^ 1);
		push_up(f), push_up(x);
	}
	void splay(int x) {
		int _x = x;
		static int S[maxn]; S[0] = 0;
		while (!chkrt(x)) S[++S[0]] = x, x = fa(x);
		S[++S[0]] = x, swap(x, _x);
		for (int i = S[0]; i; i--) push_down(S[i]);
		while (!chkrt(x)) {
			int f = fa(x), dx = chk(x), df = chk(f);
			if (chkrt(f)) rotate(x);
			else if (dx == df) rotate(f), rotate(x);
			else rotate(x), rotate(x);
		}
	}

	void access(int x) { int _x = x; for (int lst = 0; x; lst = x, x = fa(x)) splay(x), rs(x) = lst, push_up(x); splay(_x); }
	void mkrt(int x) { access(x), A[x].rev ^= 1; }
	int getrt(int x) { access(x); while (ls(x)) push_down(x = ls(x)); return x; }
	void split(int x, int y) { mkrt(x), access(y); }
	void Link(int x, int y) { mkrt(x), fa(x) = y; }
	void Cut(int x, int y) { split(x, y), fa(x) = 0, ls(y) = 0, push_up(y); }

	pii get_max(int x, int y) { split(x, y); return A[y].s; }
	void add(int x, int y, int v, int p1) {
		if (getrt(x) == getrt(y)) {
			pii res = get_max(x, y);
			if (res.fst <= v) return;
			int p2 = res.snd;
			Cut(e[p2].x, p2 + n), Cut(p2 + n, e[p2].y);
		}
		Link(x, p1 + n), Link(p1 + n, y);
		splay(p1 + n), A[p1 + n].v = mp(v, p1), push_up(p1 + n);
	}
	int check() {
		if (getrt(1) == getrt(n)) return get_max(1, n).fst;
		return inf << 2;
	}
}

int main() {
#ifdef xunzhen
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
#endif

	n = read(), m = read();
	for (int i = 1; i <= m; i++) e[i].input();
	sort(e + 1, e + m + 1);

	int ans = inf << 2;
	for (int i = 1, j = 1; i <= inf; i++) {
		while (j <= m && e[j].a <= i) LCT::add(e[j].x, e[j].y, e[j].b, j), ++j;
		chkmin(ans, i + LCT::check());
	}

	printf("%d\n", ans < (inf << 2) ? ans : -1);

	return 0;
}
19-2-11upd

这题\(Splay\)函数里的\(S\)数组其实最好要开\(maxn + maxm\) 其实也无所谓

posted @ 2019-01-25 22:09  xunzhen  阅读(125)  评论(0编辑  收藏  举报