Description
给你一个(n)个点的树,你需要支持(m)个以下(4)种操作:
-
删除树中的一条边,并且再连接一条边,满足操作之后仍然一棵树
-
往集合中加入一条路径
-
删去第(x)次往集合里加入的路径
-
询问树上的一条边是否被集合中所有的路径经过
(n leq 10^5, m leq 3 cdot 10^5)
Solution
对于操作(4),如果我们暴力的去查询集合中所有路径,那显然是不现实的。我们可以考虑每次插入一条路径后,把路径的两个端点都异或上同一个随机值
那么我们在查询的时候,就只需要查询在断掉查询边后的两棵子树中,任选其一查询其子树异或和是否等于当前集合里所有路径随机值的异或和就可以了。如果相等就说明当前集合里的所有路径都经过了查询边
至于断边连边以及子树查询操作,就可以利用LCT来实现
Code
#include <bits/stdc++.h>
using namespace std;
#define fst first
#define snd second
#define mp make_pair
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef pair<int, int> pii;
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
inline int read() {
int sum = 0, fg = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
return fg * sum;
}
typedef unsigned long long ULL;
static random_device rd;
inline ULL get_rand() { return ((ULL) rd() << 32) | rd(); }
const int maxn = 1e5 + 10;
const int maxm = 3e5 + 10;
namespace LCT {
struct node {
bool rev;
int son[2], f;
ULL v, s, x;
}A[maxn];
#define ls(x) A[x].son[0]
#define rs(x) A[x].son[1]
#define fa(x) A[x].f
inline void push_up(int x) { A[x].s = A[x].v ^ A[x].x ^ A[ls(x)].s ^ A[rs(x)].s; } /**/
inline void push_down(int x) {
if (A[x].rev) {
swap(ls(x), rs(x));
A[ls(x)].rev ^= 1, A[rs(x)].rev ^= 1, A[x].rev = 0;
}
}
inline bool chkrt(int x) { return x != ls(fa(x)) && x != rs(fa(x)); }
inline bool chk(int x) { return x == rs(fa(x)); }
inline void link(int x, int y, int f) { fa(x) = y, A[y].son[f] = x; }
inline void rotate(int x) {
int f = fa(x), dx = chk(x), df = chk(f);
link(A[x].son[dx ^ 1], f, dx);
if (!chkrt(f)) link(x, fa(f), df); else fa(x) = fa(f);
link(f, x, dx ^ 1);
push_up(f), push_up(x);
}
inline void splay(int x) {
static int S[maxn]; S[++S[0]] = x;
for (int y = x; !chkrt(y); y = fa(y)) S[++S[0]] = fa(y);
while (S[0]) push_down(S[S[0]--]);
while (!chkrt(x)) { if (!chkrt(fa(x))) rotate(chk(x) == chk(fa(x)) ? fa(x) : x); rotate(x); }
}
inline void access(int x) { int _x = x; for (int lst = 0; x; x = fa(lst = x)) splay(x), A[x].x ^= A[rs(x)].s ^ A[lst].s, rs(x) = lst, push_up(x); splay(_x); }
inline void mkrt(int x) { access(x), A[x].rev ^= 1, push_down(x); }
inline int getrt(int x) { access(x); while (ls(x)) push_down(x = ls(x)); return x; }
inline void split(int x, int y) { mkrt(x), access(y); }
inline void Link(int x, int y) { mkrt(x), access(y); fa(x) = y, A[y].x ^= A[x].s; }
inline void Cut(int x, int y) { split(x, y), fa(x) = 0, ls(y) = 0, push_up(y); }
inline void modify(int x, ULL v) { mkrt(x), A[x].v ^= v, push_up(x); }
inline ULL query(int x) { access(x); return A[x].v ^ A[x].x; }
}
ULL w[maxm], Sum;
int X[maxm], Y[maxm], cnt;
int n, m;
int main() {
#ifdef xunzhen
freopen("travel.in", "r", stdin);
freopen("travel.out", "w", stdout);
#endif
int ID = read();
n = read(), m = read();
for (int i = 1; i < n; i++) {
int x = read(), y = read();
LCT::Link(x, y);
}
for (int i = 1; i <= m; i++) {
int op = read();
if (op == 1) {
int x = read(), y = read();
LCT::Cut(x, y);
x = read(), y = read();
LCT::Link(x, y);
}
if (op == 2) {
int x = read(), y = read();
w[++cnt] = get_rand(), X[cnt] = x, Y[cnt] = y;
LCT::modify(x, w[cnt]), LCT::modify(y, w[cnt]);
Sum ^= w[cnt];
}
if (op == 3) {
int x = read();
LCT::modify(X[x], w[x]), LCT::modify(Y[x], w[x]);
Sum ^= w[x];
}
if (op == 4) {
int x = read(), y = read();
LCT::mkrt(y);
printf(LCT::query(x) == Sum ? "YES
" : "NO
");
}
}
return 0;
}