序言
U
O
J
UOJ
UOJ果然是神仙
O
J
OJ
OJ啊
里面个个都是人才,说话还好听
题目的
h
a
c
k
hack
hack数据还可以
h
a
c
k
hack
hack掉网上一大片的题解…
题目描述
为了得到书法大家的真传,小
E
E
E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个
N
N
N节点
M
M
M条边的无向图,节点标号为
1
…
n
1…n
1…n,边标号为
1
…
m
1…m
1…m。初始时小
E
E
E同学在
1
1
1 号节点,隐士则住在
n
n
n 号节点。小
E
E
E需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在
1
1
1 号节点住着两种守护精灵:
A
A
A型守护精灵与
B
B
B型守护精灵。小
E
E
E可以借助它们的力量,达到自己的目的。
只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边
e
i
e_i
ei 包含两个权值
a
i
a_i
ai 与
b
i
b_i
bi。若身上携带的
A
A
A型守护精灵个数不少于
a
i
a_i
ai,且
B
B
B型守护精灵个数不少于
b
i
b_i
bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小
E
E
E发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为
A
A
A型守护精灵的个数与
B
B
B型守护精灵的个数之和。
题目大意:
从
1
1
1走到
n
n
n,
m
m
m条边,如何走能使
a
a
a和
b
b
b的和最大值最小.
解析
首先,这是一个最小生成树的题目.
其次,这应该是一个动态最小生成树的题目.
(如果你学过用LCT做最小生成树,这个题就非常的水)
我们将
a
i
a_i
ai排序后,按边取,可以保证
a
i
a_i
ai是最小的.
在按
a
i
a_i
ai大小取边的时候,同时更新路上
b
i
b_i
bi的最大值.
当我们取到一个环的时候怎么办?
列如这样:
我们就要对新加入的边上
b
i
b_i
bi的值,和维护的链上
b
i
b_i
bi的最大值进行比较.
- 如果链上的值大,那么我们把链上最大的那条边 c u t cut cut,如何把新边连上.
- 如果新边大,我们便直接跳过.
如此往复,直到
1
1
1~
n
n
n这条路连通.
连通后,我们加入另一个操作:
判断,加入的这条边对于答案的影响.
a
a
a大并不代表
b
b
b也大,因为问的是
a
+
b
a+b
a+b的和.
直到遍历完所有边.
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF = 1e9;
const int MAXN = 2e5 + 10;
struct node {
int x, y, a, b;
bool operator < (const node &rhs) const {
return a < rhs.a;
}
}edge[MAXN];
struct vec {
int fa, son[2];
bool rev;
int maxx, id;
}p[MAXN];
int n;
int isroot(int x) {
return p[p[x].fa].son[0] != x && p[p[x].fa].son[1] != x;
}
void pushup(int x) {
int A = p[x].id, B = p[p[x].son[0]].maxx, C = p[p[x].son[1]].maxx;
if (edge[A].b >= edge[B].b&&edge[A].b >= edge[C].b)
p[x].maxx = A;
else if (edge[B].b >= edge[C].b)
p[x].maxx = B;
else
p[x].maxx = C;
}
void pushdown(int x) {
if (p[x].rev) {
int l = p[x].son[0], r = p[x].son[1];
swap(p[l].son[0], p[l].son[1]), p[l].rev ^= 1;
swap(p[r].son[0], p[r].son[1]), p[r].rev ^= 1;
p[x].rev ^= 1;
}
}
int get(int x) {
return p[p[x].fa].son[1] == x;
}
void rotate(int x) {
int y = p[x].fa, t = p[y].fa, f = get(x), k = p[x].son[f ^ 1];
p[p[k].fa = y].son[f] = p[x].son[f ^ 1];
p[x].fa = t;
if (!isroot(y))
p[t].son[get(y)] = x;
p[p[y].fa = x].son[f ^ 1] = y;
pushup(y);
}
int top, stk[MAXN];
void splay(int x) {
stk[top = 1] = x;
for (int i = x; !isroot(i); i = p[i].fa)
stk[++top] = p[i].fa;
while (top)
pushdown(stk[top--]);
for (; !isroot(x); rotate(x))
if (!isroot(p[x].fa))
rotate(get(x) ^ get(p[x].fa) ? x : p[x].fa);
pushup(x);
}
void access(int x) {
for (int i = 0; x; x = p[i = x].fa)
splay(x), p[x].son[1] = i, pushup(x);
}
void makeroot(int x) {
access(x);
splay(x);
p[x].rev ^= 1;
swap(p[x].son[0], p[x].son[1]);
}
int findroot(int x) {
access(x); splay(x);
while (p[x].son[0])
pushdown(x), x = p[x].son[0];
splay(x);
return x;
}
void split(int x, int y) {
makeroot(x);
access(y);
splay(y);
}
int link(int x, int y) {
makeroot(x);
if (findroot(y) == x)
return false;
p[x].fa = y;
return true;
}
void Cut(int x, int y) {
split(x, y);
if (p[y].son[0] == x)
p[x].fa = p[y].son[0] = 0;
}
void Addedge(int id) {
int x = edge[id].x, y = edge[id].y;
if (findroot(x) != findroot(y))
link(x, id + n), link(id + n, y);
else {
split(x, y);
if (edge[p[y].maxx].b > edge[id].b) {
int tmp = p[y].maxx;
Cut(edge[tmp].x, tmp + n), Cut(tmp + n, edge[tmp].y);
link(edge[id].x, id + n), link(id + n, edge[id].y);
}
}
}
int main() {
int m;
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> edge[i].x >> edge[i].y >> edge[i].a >> edge[i].b;
sort(edge + 1, edge + m + 1);
for (int i = 1; i <= m; i++)
p[i + n].maxx = p[i + n].id = i;
int ans = INF;
for (int i = 1; i <= m; i++) {
Addedge(i);
while (edge[i].a == edge[i + 1].a)
Addedge(++i);
if (findroot(1) == findroot(n)) {
split(1, n);
ans = min(ans, edge[p[n].maxx].b + edge[i].a);
}
}
printf("%d\n", ans == INF ? -1 : ans);
//system("pause");
return 0;
}