[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型守护精灵的个数之和。
输入
输出
输入示例
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;
}
原文:http://www.cnblogs.com/xiao-ju-ruo-xjr/p/6687798.html