【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=3669
http://uoj.ac/problem/3
【题解】
首先我们可以从小到大枚举a的最大值,然后维护b的最小生成树。显然需要用到lct,然而lct并不能维护边信息,所以我们需要把每条边变成一个点并向两头连边。每次新加入一条边
(u,v)
(
u
,
v
)
,先判断
u,v
u
,
v
是否连通,如果不连通,直接接上。否则先将这条边与原来路径上的最大值比较,若更优则替换。
时间复杂度
O((N+M)∗log(N+M))
O
(
(
N
+
M
)
∗
l
o
g
(
N
+
M
)
)
【代码】
/* - - - - - - - - - - - - - - -
User : VanishD
problem : [noi2014][bzoj3669]
Points : LCT
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define N 50010
# define M 200010
using namespace std;
int read(){
int tmp = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
return tmp * fh;
}
struct Node{
int u, v, a, b;
}eg[M];
bool cmp(Node x, Node y){
return x.a < y.a;
}
int n, m, num[M], ans;
struct lct{
struct Tree{
int pl, pr, mx, fa, tag, mxi;
}T[M];
int st[M], top;
void pushtag(int p){
if (T[p].tag != 0){
swap(T[p].pl, T[p].pr);
T[T[p].pl].tag ^= 1; T[T[p].pr].tag ^= 1;
}
T[p].tag = 0;
}
void reget(int p){
if (p == 0) return;
if (T[T[p].pl].mx >= T[T[p].pr].mx)
T[p].mx = T[T[p].pl].mx, T[p].mxi = T[T[p].pl].mxi;
else T[p].mx = T[T[p].pr].mx, T[p].mxi = T[T[p].pr].mxi;
if (num[p] > T[p].mx)
T[p].mx = num[p], T[p].mxi = p;
}
bool isroot(int p){
return (T[T[p].fa].pl != p && T[T[p].fa].pr != p);
}
void zig(int x){
int y = T[x].fa;
if (!isroot(y)){
if (T[T[y].fa].pl == y)
T[T[y].fa].pl = x; else T[T[y].fa].pr = x;
}
T[x].fa = T[y].fa;
T[y].pl = T[x].pr; T[T[x].pr].fa = y;
T[y].fa = x; T[x].pr = y;
reget(y), reget(x);
}
void zag(int x){
int y = T[x].fa;
if (!isroot(y)){
if (T[T[y].fa].pl == y)
T[T[y].fa].pl = x; else T[T[y].fa].pr = x;
}
T[x].fa = T[y].fa;
T[y].pr = T[x].pl; T[T[x].pl].fa = y;
T[y].fa = x; T[x].pl = y;
reget(y), reget(x);
}
void splay(int p){
st[top = 1] = p; int x = p;
while (!isroot(x)) st[++top] = x = T[x].fa;
for (int i = top; i >= 1; i--)
pushtag(st[i]);
x = p;
while (!isroot(x)){
int y = T[x].fa;
if (isroot(y)){
if (T[y].pl == x) zig(x);
else zag(x);
break;
}
if (T[T[y].fa].pl == y)
if (T[y].pl == x) zig(y), zig(x);
else zag(x), zig(x);
else if (T[y].pl == x) zig(x), zag(x);
else zag(y), zag(x);
}
}
void access(int x){
int v = x, u = 0;
while (v){
splay(v);
T[v].pr = u;
u = v;
v = T[v].fa;
}
splay(x);
}
void toroot(int x){
access(x);
T[x].tag ^= 1;
}
int findroot(int x){
pushtag(x);
while (T[x].pl != 0){
x = T[x].pl;
pushtag(x);
}
return x;
}
void link(int u, int v){
toroot(u);
T[u].fa = v;
}
void cut(int u, int v){
toroot(u);
access(v), splay(v);
T[v].pl = 0; T[u].fa = 0;
reget(v);
}
bool in(int u, int v){
toroot(u);
access(v);
return findroot(v) == u;
}
int findmax(int u, int v){
toroot(u);
access(v);
return T[v].mxi;
}
}a;
int main(){
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
n = read(), m = read();
for (int i = 1; i <= m; i++){
eg[i].u = read(); eg[i].v = read();
eg[i].a = read(); eg[i].b = read();
}
sort(eg + 1, eg + m + 1, cmp);
for (int i = 1; i <= m; i++)
num[i + n] = eg[i].b;
for (int i = 1; i <= n + m; i++){
a.T[i].mx = num[i];
a.T[i].mxi = i;
}
a.T[0].mx = -1;
ans = inf;
for (int i = 1; i <= m; i++){
if (eg[i].u == eg[i].v) continue;
if (a.in(eg[i].u, eg[i].v)){
int tmp = a.findmax(eg[i].u, eg[i].v);
if (num[tmp] >= num[i + n]){
a.cut(eg[tmp - n].u, tmp);
a.cut(eg[tmp - n].v, tmp);
a.link(eg[i].u, i + n);
a.link(eg[i].v, i + n);
}
}
else {
a.link(eg[i].u, i + n);
a.link(eg[i].v, i + n);
}
if (a.in(1, n))
ans = min(ans, eg[i].a + num[a.findmax(1, n)]);
}
printf("%d\n", (ans == inf) ? -1 : ans);
return 0;
}