首页 > 代码库 > [BZOJ3669][Noi2014]魔法森林

[BZOJ3669][Noi2014]魔法森林

[BZOJ3669][Noi2014]魔法森林

试题描述

为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

输入

第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

输出

输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

输入示例

4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17

输出示例

3 1
1 2 1 1

数据规模及约定

2<=n<=50,000

0<=m<=100,000

1<=ai ,bi<=50,000

题解

首先按权值 a 排序,然后动态加入这些边,如果形成环了则把环上权值 b 最大的边删去。这样我们就可以保证在所有权值 a 不超过当前的 ai 时 bi 最小了,所以我们每次加完边后都求一次 1 到 n 的路径上的答案即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxn 50010
#define maxm 100010
#define maxnode 150010
#define oo 2147483647

struct Node {
	int a, b, mxa, mxb;
	bool rev;
	Node(): rev(0) {}
	Node(int _, int __): a(_), b(__) {}
} ns[maxnode];
int fa[maxnode], ch[maxnode][2], S[maxnode], top;
bool isrt(int u) { return !fa[u] || (ch[fa[u]][0] != u && ch[fa[u]][1] != u); }
void maintain(int o) {
	ns[o].mxa = ns[o].mxb = o;
	for(int i = 0; i < 2; i++) if(ch[o][i]) {
		int son = ns[ch[o][i]].mxa, &mea = ns[o].mxa;
		if(ns[son].a > ns[mea].a) mea = son;
		son = ns[ch[o][i]].mxb; int &meb = ns[o].mxb;
		if(ns[son].b > ns[meb].b) meb = son;
	}
	return ;
}
void pushdown(int o) {
	if(!ns[o].rev) return ;
	swap(ch[o][0], ch[o][1]);
	for(int i = 0; i < 2; i++) if(ch[o][i])
		ns[ch[o][i]].rev ^= 1;
	ns[o].rev = 0;
	return ;
}
void rotate(int u) {
	int y = fa[u], z = fa[y], l = 0, r = 1;
	if(!isrt(y)) ch[z][ch[z][1]==y] = u;
	if(ch[y][1] == u) swap(l, r);
	fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
	ch[y][l] = ch[u][r]; ch[u][r] = y;
	maintain(y); maintain(u);
	return ;
}
void splay(int u) {
	int t = u; S[top = 1] = t;
	while(!isrt(t)) S[++top] = fa[t], t = fa[t];
	while(top) pushdown(S[top--]);
	while(!isrt(u)) {
		int y = fa[u], z = fa[y];
		if(!isrt(y)) {
			if(ch[y][0] == u ^ ch[z][0] == y) rotate(u);
			else rotate(y);
		}
		rotate(u);
	}
	return ;
}
void access(int u) {
	splay(u); ch[u][1] = 0; maintain(u);
	while(fa[u]) splay(fa[u]), ch[fa[u]][1] = u, maintain(fa[u]), splay(u);
	return ;
}
void makeroot(int u) {
	access(u); ns[u].rev ^= 1;
	return ;
}
void link(int a, int b) {
	makeroot(b); fa[b] = a;
	return ;
}
void cut(int a, int b) {
	makeroot(a); access(b); ch[b][0] = fa[a] = 0; maintain(a);
	return ;
}
int _a, _b;
void query(int a, int b) {
	makeroot(a); access(b);
	_a = ns[b].mxa; _b = ns[b].mxb;
	return ;
}

struct Edge {
	int u, v, a, b;
	Edge() {}
	Edge(int _1, int _2, int _3, int _4): u(_1), v(_2), a(_3), b(_4) {}
	bool operator < (const Edge& t) const { return a < t.a; }
} es[maxm];

int pa[maxn];
int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); }

int ans;

int main() {
	int n = read(), m = read();
	for(int i = 1; i <= n; i++) ns[i] = Node(0, 0), maintain(i), pa[i] = i;
	for(int i = 1; i <= m; i++) {
		int u = read(), v = read(), a = read(), b = read();
		es[i] = Edge(u, v, a, b);
	}
	sort(es + 1, es + m + 1);
	
	ans = oo;
//	for(int i = 1; i <= m; i++) printf("Edge %d: %d %d %d %d\n", i, es[i].u, es[i].v, es[i].a, es[i].b);
	for(int i = 1; i <= m; i++) {
		int u = es[i].u, v = es[i].v, U = findset(u), V = findset(v);
		ns[i+n] = Node(es[i].a, es[i].b); maintain(i + n);
		if(U == V) {
			query(u, v);
			if(ns[_b].b > es[i].b) {
				cut(es[_b-n].u, _b); cut(_b, es[_b-n].v);
				link(u, i + n); link(i + n, v);
			}
		}
		else {
			pa[V] = U;
			link(u, i + n); link(i + n, v);
		}
		if(findset(1) == findset(n)) {
			query(1, n);
			ans = min(ans, ns[_a].a + ns[_b].b);
		}
	}
	
	printf("%d\n", ans < oo ? ans : -1);
	
	return 0;
}

 

[BZOJ3669][Noi2014]魔法森林