3754. 【NOI2014】魔法森林(LCT)

Problem

给定一个\(n\)个结点,\(m\)条边的的无向图,每条边有两个权值\(ai,bi\).

现在从\(1\)出发,要到达\(n\),每次只能沿着\(ai\le A\)\(bi\le B\)的边走,求\(min(A+B)\).

\(n\le 5*10^4,m\le 2*10^5\)

Solution

经典的LCT题,拆边权为点权就没了.

顺便说一下LCT这个神奇的数据结构。因为姿势有限,暂且不讨论它的势能分析。

定义

LCT = splay + 虚实剖分

每个节点有 至多一条 实边连向儿子, 实边 相连,构成了一条 实链.

对于每条实链用一个 splay 去维护,关键字就是在原树当中以 根节点 为基准的 深度

Access

最重要的操作.

Access(N)定义为改变从\(N\)到根节点这一段路的每个节点的实边.

在代码中用这个过程实现:

void Access(int x) {
	for (int y = 0; x ; y = x, x = fa[x])
		splay(x), tr[x][1] = y, update(x);
}
Makeroot

定义为把某个点设为根,用这个过程去实现:

void Makeroot(int x) {
	Access(x);
	splay(x);
	reverse(x);
}
Link,cut

就显得很简单了

void link(int x,int y) {
	Makeroot(x);
	fa[x] = y;
}
void cut(int x, int y) {
	Makeroot(x);
	Access(y);
	splay(y);
	fa[tr[y][0]] = 0, tr[y][0] = 0;
	update(y);
}
注意

rotate的打法必须要记住这样打:

void rotate(int x) {
	int y = fa[x], t = son(x);
	if (!Rt(y)) tr[fa[y]][son(y)] = x; // 这里一定要注意!!!
	if (tr[x][1 - t]) fa[tr[x][1 - t]] = y;
	fa[x] = fa[y], fa[y] = x, tr[y][t] = tr[x][1 - t], tr[x][1 - t] = y;
	update(y), update(x); 
}

Code
#include <bits/stdc++.h>

#define F(i, a, b) for (int i = a; i <= b; i ++)
#define ls tr[x][0]
#define rs tr[x][1]

const int N = 2e5 + 10;

using namespace std;

int n, m, x, y, Ans, RT;
int v[N], tr[N][2], fa[N], rev[N], d[N], pt[N], fat[N];
struct edge {
	int x, y, a, b;
	friend bool operator < (edge a, edge b) { return a.a < b.a; }
} e[N];

int son(int x) { return tr[fa[x]][1] == x; }
bool Rt(int x) { return tr[fa[x]][son(x)] ^ x; }

void reverse(int x) { swap(ls, rs), rev[x] ^= 1; }
void push(int x) {
	if (!rev[x]) return;
	reverse(ls), reverse(rs), rev[x] = 0;
}

void update(int x) {
	pt[x] = x;
	if (v[pt[ls]] > v[pt[x]]) pt[x] = pt[ls];
	if (v[pt[rs]] > v[pt[x]]) pt[x] = pt[rs];
}

void rotate(int x) {
	int y = fa[x], t = son(x);
	if (!Rt(y)) tr[fa[y]][son(y)] = x;
	if (tr[x][1 - t]) fa[tr[x][1 - t]] = y;
	fa[x] = fa[y], fa[y] = x, tr[y][t] = tr[x][1 - t], tr[x][1 - t] = y;
	update(y), update(x); 
}

void clear(int x) {
	d[++ d[0]] = x;
	while (!Rt(x)) d[++ d[0]] = x = fa[x];
	while (d[0]) push(d[d[0] --]);
}

void splay(int x) {
	clear(x);
	for (; !Rt(x); rotate(x))
		if (!Rt(fa[x])) rotate(son(x) == son(fa[x]) ? fa[x] : x);
}

void Access(int x) {
	for (int y = 0; x ; y = x, x = fa[x])
		splay(x), rs = y, update(x);
}

void Makeroot(int x) { Access(x), splay(x), reverse(x); }
void link(int x, int y) { Makeroot(x), fa[x] = y; }
void cut(int x, int y) { Makeroot(x); Access(y); splay(y); fa[tr[y][0]] = 0, tr[y][0] = 0; update(y); }

int Solve(int x, int y) {
	Makeroot(x); Access(y), splay(y);
	return pt[y];
}
int get(int x) { return !fat[x] ? x : fat[x] = get(fat[x]); }

int main() {
	scanf("%d%d", &n, &m);
	F(i, 1, m) {
		scanf("%d%d%d%d", &e[i].x, &e[i].y, &e[i].a, &e[i].b);
		if (e[i].x == e[i].y) i --, m --;
	}

	sort(e + 1, e + m + 1);

	Ans = 1e9;
	F(i, 1, m) {
		x = e[i].x, y = e[i].y, v[i + n] = e[i].b;
		int xx = get(x), yy = get(y);
		if (xx ^ yy) fat[xx] = yy; else {
			int pos = Solve(x, y);
			if (v[pos] <= e[i].b) continue;
			cut(e[pos - n].x, pos), cut(pos, e[pos - n].y);
		}
		link(x, i + n), link(i + n, y);
		if (get(1) == get(n))
			Ans = min(Ans, e[i].a + v[Solve(1, n)]);
	}
	printf("%d\n", Ans == 1e9 ? - 1 : Ans);
}
posted @ 2019-04-24 09:01  proking  阅读(242)  评论(0编辑  收藏  举报