[BZOJ-3669]魔法森林

Description

给你一张n个点m条边的无向图,每条边有两个属性a和b。
你需要找到一条从1~n的路径,使得这些边的第一个属性最大值加上第二个属性最大值的值最小。

Solution

这是一道[NOI2014]的题。
我们先把边按a的值从小到大排序,然后我们依次扫过每一条边。
这样我们当前边的a属性最大值是可以确定的,我们就要让b属性的最大值尽量小。
我们每扫描一条边,如果这条边的两端u,v已经相连了,那我们找到u到v的路径上b属性最大的边e。
如果e的b比当前的b要大,那么就割掉e与两端端点之间的连边,连接当前边与u,v的连边。
这样就保证了不会有环出现。
如果这条边两端u,v不相连,直接与u,v相连即可。
我们可以用LCT维护这个东西,把边权转化为点权。

Notice

我们用LCT维护b属性最大的边的序号可以更方便,而不是维护最大的b属性值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define sqz main
#define ll long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define Rep(i, a, b) for (int i = (a); i < (b); i++)
#define travel(i, u) for (int i = head[u]; ~i; i = edge[i].next)

const ll INF = 1e9, Mo = 998244353;
const int N = 50000, M = 100000;
const double eps = 1e-6;
namespace slow_IO
{
ll read()
{
ll x = 0; int zf = 1; char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * zf;
}
void write(ll y)
{
if (y < 0) putchar('-'), y = -y;
if (y > 9) write(y / 10);
putchar(y % 10 + '0');
}
}
using namespace slow_IO;

struct node
{
int u, v, a, b;
node() {}
node(int _u, int _v, int _a, int _b) {u = _u, v = _v, a = _a, b = _b;}
}Edge[M + 5];
inline int cmp(node X, node Y)
{
return X.a < Y.a;
}

struct LinkCutTree
{
int Val[N + M + 5], Fa[N + M + 5], Son[N + M + 5][2], rev[N + M + 5], Max[N + M + 5], Stack[N + M + 5], top;
inline void up(int u)
{
Max[u] = u;
if (Son[u][0] && Val[Max[Son[u][0]]] > Val[Max[u]]) Max[u] = Max[Son[u][0]];
if (Son[u][1] && Val[Max[Son[u][1]]] > Val[Max[u]]) Max[u] = Max[Son[u][1]];
}
inline void down(int u)
{
if (!rev[u]) return;
rev[Son[u][0]] ^= 1, rev[Son[u][1]] ^= 1, rev[u] ^= 1;
swap(Son[u][0], Son[u][1]);
}
inline int isroot(int u)
{
return (Son[Fa[u]][0] != u && Son[Fa[u]][1] != u);
}

inline void Rotate(int x)
{
int y = Fa[x], z = Fa[y];
int l = Son[y][1] == x, r = l ^ 1;
if (!isroot(y)) Son[z][Son[z][1] == y] = x;
Son[y][l] = Son[x][r], Fa[Son[x][r]] = y;
Son[x][r] = y, Fa[y] = x, Fa[x] = z;
up(y); up(x);
}
inline void Splay(int x)
{
Stack[top = 1] = x;
for (int y = x; !isroot(y); y = Fa[y]) Stack[++top] = Fa[y];
per(i, top, 1) down(Stack[i]);
while (!isroot(x))
{
int y = Fa[x], z = Fa[y];
if (!isroot(y))
{
if ((Son[z][1] == y) ^ (Son[y][1] == x)) Rotate(x);
Rotate(y);
}
Rotate(x);
}
}

inline void Access(int u)
{
for (int last = 0; u; last = u, u = Fa[u])
Splay(u), Son[u][1] = last, up(u);
}
inline void Make_Root(int u)
{
Access(u), Splay(u), rev[u] ^= 1;
}
inline int Find_Root(int u)
{
Access(u), Splay(u);
while (Son[u][0]) u = Son[u][0];
return u;
}

inline void Split(int x, int y)
{
Make_Root(x), Access(y), Splay(y);
}
inline void Link(int x, int y)
{
Make_Root(x);
if (Find_Root(y) == x) return;
Fa[x] = y;
}
inline void Cut(int x, int y)
{
Make_Root(x);
if (Find_Root(y) != x || Fa[x] != y || Son[x][1]) return;
Fa[x] = Son[y][0] = 0;
}
}LCT;

int sqz()
{
int n = read(), m = read(), cnt = 0, ans = INF;
rep(i, 1, m)
{
int u = read(), v = read(), a = read(), b = read();
if (u == v) continue;
Edge[++cnt] = node(u, v, a, b);
}
sort(Edge + 1, Edge + cnt + 1, cmp);
rep(i, 1, cnt)
{
LCT.Val[n + i] = Edge[i].b;
if (LCT.Find_Root(Edge[i].u) == LCT.Find_Root(Edge[i].v))
{
LCT.Split(Edge[i].u, Edge[i].v); int x = LCT.Max[Edge[i].v] - n;
if (LCT.Val[x + n] > Edge[i].b)
{
LCT.Cut(x + n, Edge[x].u), LCT.Cut(x + n, Edge[x].v);
LCT.Link(i + n, Edge[i].u), LCT.Link(i + n, Edge[i].v);
}
}
else LCT.Link(i + n, Edge[i].u), LCT.Link(i + n, Edge[i].v);
if (LCT.Find_Root(1) == LCT.Find_Root(n)) ans = min(ans, Edge[i].a + (LCT.Split(1, n), LCT.Val[LCT.Max[n]]));
}
printf("%d\n", ans == INF ? -1 : ans);
return 0;
}
文章目录
  1. 1. Description
  2. 2. Solution
  3. 3. Notice
  4. 4. Code