总览:
用 splay 动态维护树的信息
支持加边断边,换根等操作
可以维护连通性
板子:
struct LCT {
int ch[2], f;
int rev;
} tr[A];
#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
inline int isroot(int x) { return ls(tr[x].f) != x && rs(tr[x].f) != x; }
inline void pushup(int x) {
//
return;
}
inline void reverse(int x) {
if (x) swap(ls(x), rs(x)), tr[x].rev ^= 1;
}
inline void pushdown(int x) {
if (tr[x].rev) reverse(ls(x)), reverse(rs(x)),tr[x].rev ^= 1;
}
inline void rotate(int x) {
int y = tr[x].f, z = tr[y].f;
int k = (rs(y) == x);
if (z && !isroot(y)) tr[z].ch[rs(z) == y] = x;
tr[x].f = z, tr[y].ch[k] = tr[x].ch[k ^ 1];
tr[tr[x].ch[k ^ 1]].f = y;
tr[x].ch[k ^ 1] = y, tr[y].f = x;
pushup(y);
return;
}
int st[A], top;
inline void pushpath(int x) {
top = 0;
st[++top] = x;
for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
for (int i = top; i; i--) pushdown(st[i]);
return;
}
inline void splay(int x) {
pushpath(x);
while (!isroot(x)) {
int y = tr[x].f, z = tr[y].f;
if (!isroot(y)) {
if ((rs(y) == x) == (rs(z) == y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
pushup(x);
return;
}
inline void access(int x) {
for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y, pushup(x);
}
inline int findroot(int x) {
access(x), splay(x);
while (ls(x)) pushdown(x), x = ls(x);
return x;
}
inline void makeroot(int x) { access(x), splay(x), reverse(x); }
inline void split(int x, int y) { makeroot(x), access(y), splay(y); }
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) tr[x].f = y;
}
inline void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
pushup(y);
}
#undef ls
#undef rs
splay 后 pushdown,改变儿子后 pushup
T1 P2147 [SDOI2008]洞穴勘测
思路:
LCT 维护连通性板子
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
inline char ch() {
static char buf[1 << 21], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
? EOF
: *p1++;
}
inline int in() {
int s = 0, f = 1;
char x;
for (x = getchar(); x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
} // namespace IO
using namespace IO;
const int A = 4e5 + 5;
int n, m;
struct LCT {
int ch[2], f, rev;
LCT() { ch[0] = ch[1] = f = rev = 0; }
} tr[A];
#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
inline int isroot(int x) {
return !(tr[x].f && (ls(tr[x].f) == x || rs(tr[x].f) == x));
}
inline void reverse(int x) {
if (x) {
swap(ls(x), rs(x));
tr[x].rev ^= 1;
}
return;
}
inline void pushdown(int x) {
if (tr[x].rev) {
reverse(ls(x));
reverse(rs(x));
tr[x].rev ^= 1;
}
return;
}
inline void rotate(int x) {
int y = tr[x].f, z = tr[y].f;
int k = (rs(y) == x);
if (!isroot(y)) tr[z].ch[rs(z) == y] = x;
tr[x].f = z;
tr[y].ch[k] = tr[x].ch[k ^ 1];
if (tr[x].ch[k ^ 1]) tr[tr[x].ch[k ^ 1]].f = y;
tr[x].ch[k ^ 1] = y, tr[y].f = x;
return;
}
int st[A], top;
inline void pushpath(int x) {
top = 0;
st[++top] = x;
for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
for (int i = top; i; i--) pushdown(st[i]);
return;
}
inline void splay(int x) {
pushpath(x);
while (!isroot(x)) {
int y = tr[x].f, z = tr[y].f;
if (!isroot(y)) {
if ((rs(y) == x) == (rs(z) == y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
return;
}
inline void access(int x) {
for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y;
return;
}
inline int findroot(int x) {
access(x), splay(x), pushdown(x);
while (ls(x)) x = ls(x), pushdown(x);
return x;
}
inline void makeroot(int x) {
access(x), splay(x), reverse(x);
return;
}
inline void split(int x, int y) {
makeroot(x), access(y), splay(y);
return;
}
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) tr[x].f = y;
return;
}
inline void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
return;
}
#undef ls
#undef rs
signed main() {
n = in(), m = in();
while (m--) {
char x = getchar();
while (x != 'Q' && x != 'C' && x != 'D') x = getchar();
if (x == 'Q') {
int u = in(), v = in();
makeroot(u);
puts(findroot(v) == u ? "Yes" : "No");
} else if (x == 'C') {
int u = in(), v = in();
link(u, v);
} else {
int u = in(), v = in();
cut(u, v);
}
}
return 0;
}
T2 P3690 【模板】Link Cut Tree (动态树)
思路:板子
代码:
#include <bits/stdc++.h>
using namespace std;
#define re register
namespace IO {
inline char ch() {
static char buf[1 << 21], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
? EOF
: *p1++;
}
inline int in() {
int s = 0, f = 1;
char x;
for (x = ch(); x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
} // namespace IO
using namespace IO;
const int A = 3e5 + 5;
int n, m;
struct LCT {
int ch[2], f, rev;
int val, sum;
} tr[A];
#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
inline int isroot(int x) { return !(ls(tr[x].f) == x || rs(tr[x].f) == x); }
inline void pushup(int x) {
tr[x].sum = tr[ls(x)].sum ^ tr[rs(x)].sum ^ tr[x].val;
return;
}
inline void reverse(int x) {
if (x) {
swap(ls(x), rs(x));
tr[x].rev ^= 1;
}
return;
}
inline void pushdown(int x) {
if (tr[x].rev) {
reverse(ls(x)), reverse(rs(x));
tr[x].rev ^= 1;
}
return;
}
inline void rotate(int x) {
int y = tr[x].f, z = tr[y].f;
int k = (rs(y) == x);
if (!isroot(y)) tr[z].ch[rs(z) == y] = x;
tr[x].f = z, tr[y].ch[k] = tr[x].ch[k ^ 1];
tr[tr[x].ch[k ^ 1]].f = y;
tr[x].ch[k ^ 1] = y, tr[y].f = x;
pushup(y), pushup(x);
return;
}
int st[A], top;
inline void pushpath(int x) {
top = 0;
st[++top] = x;
for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
for (int i = top; i; i--) pushdown(st[i]);
return;
}
inline void splay(int x) {
pushpath(x);
while (!isroot(x)) {
int y = tr[x].f, z = tr[y].f;
if (!isroot(y)) {
if ((rs(y) == x) == (rs(z) == y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
pushup(x);
return;
}
inline void access(int x) {
for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y, pushup(x);
return;
}
inline int findroot(int x) {
access(x), splay(x);
while (ls(x)) pushdown(x), x = ls(x);
return x;
}
inline void makeroot(int x) {
access(x), splay(x), reverse(x);
return;
}
inline void split(int x, int y) {
makeroot(x), access(y), splay(y);
return;
}
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) tr[x].f = y;
return;
}
inline void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
pushup(y);
return;
}
#undef ls
#undef rs
signed main() {
n = in(), m = in();
for (int i = 1; i <= n; i++) tr[i].val = in();
while (m--) {
int opt = in();
if (opt == 0) {
int u = in(), v = in();
split(u, v);
printf("%d\n", tr[v].sum);
} else if (opt == 1) {
int u = in(), v = in();
link(u, v);
} else if (opt == 2) {
int u = in(), v = in();
cut(u, v);
} else {
int u = in(), v = in();
splay(u);
tr[u].val = v;
}
}
return 0;
}
T3 P2387 [NOI2014]魔法森林
思路:
考虑类似最小生成树的做法,加边动态维护
先将边按一种边权排序
依次加边维护另一种边权的最小生成树
每次更新答案
可以用 LCT 维护最小生成树
如果成环就找到环上边权最大的边比较大小
需要将边化作点,如果加入边,就将这个点和两端点相连
代码:
#include <bits/stdc++.h>
using namespace std;
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch() \
(_p1 == _p2 && \
(_p2 = (_p1 = buf) + fread(buf, 1, 1 << 21, stdin), _p1 == _p2) \
? EOF \
: *_p1++)
inline int in() {
int s = 0, f = 1;
char x = getchar();
while (x < '0' || x > '9') {
x = getchar();
if (x == '-') f = -1;
}
while (x >= '0' && x <= '9') {
s = (s * 10) + (x & 15);
x = getchar();
}
return f == 1 ? s : -s;
}
char _buf_[1 << 21];
int _p1_ = -1;
inline void flush() {
fwrite(_buf_, 1, _p1_ + 1, stdout);
_p1_ = -1;
}
inline void pc(char x) {
if (_p1_ == (1 << 21) - 1) flush();
_buf_[++_p1_] = x;
}
inline void out(int x) {
if (!x) {
pc('0');
return;
}
if (x < 0) {
pc('-');
x = -x;
}
char k[20];
int tot = 0;
while (x) {
k[++tot] = (x % 10) | 48;
x /= 10;
}
for (int i = tot; i; i--) pc(k[i]);
return;
}
inline void out(string x) {
int len = x.size();
for (int i = 0; i < len; i++) pc(x[i]);
}
} // namespace IO
using namespace IO;
const int A = 2e5 + 5;
const int INF = 1e9;
int n, m;
struct Road {
int x, y, a, b;
} p[A];
inline bool cmp(Road u, Road v) { return u.b < v.b; }
inline int d(int x) { return x + n; }
int f[A];
inline int find(int x) { return f[x] == x ? f[x] : f[x] = find(f[x]); }
inline void merge(int x, int y) {
int xx = find(x), yy = find(y);
if (xx != yy) f[xx] = yy;
return;
}
struct LCT {
int ch[2], f;
int rev, val, pos;
} tr[A];
#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
#define max(x) tr[tr[x].pos].val
inline int isroot(int x) { return ls(tr[x].f) != x && rs(tr[x].f) != x; }
inline void pushup(int x) {
tr[x].pos = x;
if (ls(x) && max(ls(x)) > max(x))
tr[x].pos = tr[ls(x)].pos;
if (rs(x) && max(rs(x)) > max(x))
tr[x].pos = tr[rs(x)].pos;
return;
}
inline void reverse(int x) {
if (x) {
swap(ls(x), rs(x));
tr[x].rev ^= 1;
}
}
inline void pushdown(int x) {
if (tr[x].rev) {
reverse(ls(x)), reverse(rs(x));
tr[x].rev ^= 1;
}
return;
}
inline void rotate(int x) {
int y = tr[x].f, z = tr[y].f;
int k = (rs(y) == x);
if (!isroot(y)) tr[z].ch[rs(z) == y] = x;
tr[x].f = z, tr[y].ch[k] = tr[x].ch[k ^ 1];
tr[tr[x].ch[k ^ 1]].f = y;
tr[x].ch[k ^ 1] = y, tr[y].f = x;
pushup(y), pushup(x);
return;
}
int st[A], top;
inline void pushpath(int x) {
top = 0;
st[++top] = x;
for (int i = x; !isroot(i); i = tr[i].f) st[++top] = tr[i].f;
for (int i = top; i; i--) pushdown(st[i]);
return;
}
inline void splay(int x) {
pushpath(x);
while (!isroot(x)) {
int y = tr[x].f, z = tr[y].f;
if (!isroot(y)) {
if ((rs(y) == x) == (rs(z) == y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
pushup(x);
return;
}
inline void access(int x) {
for (int y = 0; x; y = x, x = tr[x].f) splay(x), rs(x) = y, pushup(x);
}
inline int findroot(int x) {
access(x), splay(x);
while (ls(x)) pushdown(x), x = ls(x);
pushdown(x);
return x;
}
inline void makeroot(int x) { access(x), splay(x), reverse(x); }
inline void split(int x, int y) { makeroot(x), access(y), splay(y); }
inline void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) tr[x].f = y;
return;
}
inline void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && tr[x].f == y && ls(y) == x) tr[x].f = 0, ls(y) = 0;
pushup(y);
return;
}
inline int qurey(int x, int y) {
split(x, y);
return tr[y].pos;
}
#undef ls
#undef rs
#define max
int ans = INF;
signed main() {
n = in(), m = in();
for (int i = 1; i <= m; i++)
p[i].x = in(), p[i].y = in(), p[i].a = in(), p[i].b = in();
sort(p + 1, p + 1 + m, cmp);
for (int i = 1; i <= n + m; i++) f[i] = i, tr[i].pos = i;
for (int i = 1; i <= m; i++) tr[d(i)].val = p[i].a;
for (int i = 1; i <= m; i++) {
int x = p[i].x, y = p[i].y, pos = 0;
if (find(x) == find(y)) {
int w = qurey(x, y);
if (tr[w].val > p[i].a)
cut(p[w - n].x, w), cut(w, p[w - n].y);
else
pos = 1;
} else {
merge(x, y);
}
if (!pos) link(x, d(i)), link(d(i), y);
if (find(1) == find(n)) ans = min(ans, p[i].b + tr[qurey(1, n)].val);
}
if (ans == INF)
out(-1);
else
out(ans);
pc('\n');
flush();
return 0;
}