bzoj 3669 [Noi2014]魔法森林 LCT
题面
解法
维护双关键字好像不太可做吧
所以我们先按照\(a\)从小到大排序,然后逐一检查\(b\)
如果构成环,那么把环上最大的\(b\)删掉
只要出现1和\(n\)连通的时候就更新答案
时间复杂度:\(O((n+m)\ log\ n)\)
代码
#include <bits/stdc++.h>
#define N 150010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
int x, y, p, q;
bool operator < (const Edge &a) const {
return p < a.p;
}
} a[N];
namespace LCT {
struct Node {
int fa, num, rev, val, pathfa, child[2];
} t[N];
int son(int x, int y) {return t[x].child[1] == y;}
int val(int x) {if (!x) return 0; return t[x].val;}
int num(int x) {if (!x) return 0; return t[x].num;}
void rev(int x) {if (!x) return; t[x].rev ^= 1;}
void update(int x) {
if (!x) return; t[x].num = x;
if (val(t[x].num) < val(num(t[x].child[0]))) t[x].num = num(t[x].child[0]);
if (val(t[x].num) < val(num(t[x].child[1]))) t[x].num = num(t[x].child[1]);
}
void pushdown(int x) {
if (!x) return;
int k = t[x].rev;
if (k) {
rev(x), swap(t[x].child[0], t[x].child[1]);
rev(t[x].child[0]), rev(t[x].child[1]);
}
}
void Connect(int x, int y, int k) {
if (x) t[x].child[k] = y;
if (y) t[y].fa = x; update(x);
}
void Rotate(int x) {
int y = t[x].fa, z = t[y].fa;
pushdown(y), pushdown(x);
swap(t[x].pathfa, t[y].pathfa);
int a = son(y, x), b = !a;
Connect(z, x, son(z, y));
Connect(y, t[x].child[b], a);
Connect(x, y, b);
update(y), update(x);
}
void Splay(int x) {
while (t[x].fa) {
int y = t[x].fa, z = t[y].fa;
if (z) {
pushdown(z), pushdown(y);
(son(z, y) ^ son(y, x)) ? Rotate(x) : Rotate(y);
}
Rotate(x);
}
}
void expose(int x) {
Splay(x), pushdown(x);
int y = t[x].child[1];
t[y].fa = 0, t[y].pathfa = x;
t[x].child[1] = 0, update(x);
}
void access(int x) {
for (expose(x); t[x].pathfa; Splay(x)) {
expose(t[x].pathfa);
Connect(t[x].pathfa, x, 1);
t[x].pathfa = 0;
}
}
void evert(int x) {access(x), Splay(x), rev(x);}
void link(int x, int y) {evert(y), t[y].pathfa = x;}
void cut(int x, int y) {
evert(x), access(y);
Splay(y), pushdown(y);
t[t[y].child[0]].fa = 0;
t[y].child[0] = 0, update(y);
}
int query(int x, int y) {
evert(x); access(y); Splay(y);
return t[y].num;
}
}
int p[N];
int Find(int x) {
if (p[x] == x) return x;
return p[x] = Find(p[x]);
}
int main() {
using namespace LCT;
int n, m; read(n), read(m);
for (int i = 1; i <= n; i++) t[i].num = i;
for (int i = 1; i <= m; i++)
read(a[i].x), read(a[i].y), read(a[i].p), read(a[i].q);
sort(a + 1, a + m + 1); int ans = INT_MAX;
for (int i = n + 1; i <= n + m; i++) t[i].val = a[i - n].q;
for (int i = 1; i <= n + m; i++) t[i].num = i;
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 1; i <= m; i++) {
int tx = Find(a[i].x), ty = Find(a[i].y);
if (tx != ty) {
p[tx] = ty;
link(a[i].x, i + n), link(a[i].y, i + n);
} else {
int tmp = query(a[i].x, a[i].y);
if (val(tmp) > a[i].q) {
int pos = tmp - n, x = a[pos].x, y = a[pos].y;
cut(x, tmp), cut(y, tmp);
link(a[i].x, i + n), link(a[i].y, i + n);
}
}
if (Find(1) == Find(n)) {
int tmp = query(1, n);
chkmin(ans, a[i].p + val(tmp));
}
}
if (ans != INT_MAX) cout << ans << "\n";
else cout << "-1\n";
return 0;
}