uoj207 LCT维护子树信息+随机化

(8 mins to read)

给定一棵树,要求支持加边删边,并维护一个点对集合,支持加入和删除点对,并询问一条边是否在所有集合中的点对间的路径上.

做法

考虑询问路径x和y:当x为根且集合中每个点对的其中一个都在y子树内时合法.给子树内每个点对随机一个权值,然后维护子树的异或和信息,当y子树内的异或和等于整个集合的异或和时合法.由于动态加边和删边所以用lct维护,对于子树信息,只要额外维护一下每个点虚儿子的异或和信息即可.

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
155
156
157
158
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) begin(x), end(x)
#define fi first
#define se second
#define debug(x) cerr << #x << " " << x << '\n'
using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pli = pair<ll,int>;
const int INF = 0x3f3f3f3f, N = 3e5 + 5;
const ll LINF = 1e18 + 5;
constexpr int mod = 1e9 + 7;
int id, n, m, tot, w[N];
pii p[N];
struct LCT
{
int ch[N][2], fa[N], rev[N];
int sum[N], sum2[N], val[N];
void clear(int x)
{
ch[x][0] = ch[x][1] = fa[x] = rev[x] = 0;
sum[x] = sum2[x] = val[x] = 0;
}
int getch(int x) { return ch[fa[x]][1]==x; }
int noroot(int x) { return ch[fa[x]][0]==x || ch[fa[x]][1]==x; }
void pushup(int x)
{
if(x) sum[x] = sum[ch[x][0]] ^ sum[ch[x][1]] ^ sum2[x] ^ val[x];
}
void reverse(int x)
{
swap(ch[x][0], ch[x][1]);
rev[x] ^= 1;
}
void pushdown(int x)
{
if(rev[x])
{
if(ch[x][0]) reverse(ch[x][0]);
if(ch[x][1]) reverse(ch[x][1]);
rev[x] = 0;
}
}
void pushall(int x)
{
if(noroot(x)) pushall(fa[x]);
pushdown(x);
}
void rotate(int x)
{
int y = fa[x], z = fa[y], chx = getch(x), chy = getch(y);
fa[x] = z;
if(noroot(y)) ch[z][chy] = x;
ch[y][chx] = ch[x][chx^1];
fa[ch[x][chx^1]] = y;
ch[x][chx^1] = y;
fa[y] = x;
pushup(y);
pushup(x);
pushup(z);
}
void splay(int x)
{
pushall(x);
for(int f=fa[x]; f=fa[x], noroot(x); rotate(x))
if(noroot(f)) rotate(getch(x) == getch(f) ? f : x);
}
void access(int x)
{
for(int y=0; x; x=fa[y=x])
{
splay(x);
sum2[x] ^= sum[ch[x][1]] ^ sum[y];
ch[x][1] = y;
pushup(x);
}
}
void makeroot(int x)
{
access(x); splay(x);
reverse(x);
}
int findroot(int x)
{
access(x); splay(x);
while(ch[x][0]) pushdown(x), x = ch[x][0];
splay(x);
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y); splay(y);
}
void link(int x,int y)
{
makeroot(x);
makeroot(y);
fa[x] = y;
sum2[y] ^= sum[x];
}
void cut(int x,int y)
{
split(x,y);
fa[x] = ch[y][0]=0;
pushup(y);
}
}T;
int all;
int main()
{
srand(114514);
scanf("%d%d%d", &id, &n, &m);
for(int i=1; i<n; i++)
{
int u, v; scanf("%d%d", &u, &v);
T.link(u, v);
}
while(m--)
{
int op, x;
scanf("%d%d", &op, &x);
if(op==1)
{
int y, u, v;
scanf("%d%d%d", &y, &u, &v);
T.cut(x, y); T.link(u, v);
}
else if(op==2)
{
int y;
scanf("%d", &y);
p[++tot] = {x, y};
w[tot] = rand();
T.makeroot(x); T.val[x] ^= w[tot];
T.makeroot(y); T.val[y] ^= w[tot];
all ^= w[tot];
}
else if(op==3)
{
T.makeroot(p[x].fi); T.val[p[x].fi] ^= w[x];
T.makeroot(p[x].se); T.val[p[x].se] ^= w[x];
all ^= w[x];
}
else
{
int y;
scanf("%d", &y);
T.split(x, y);
if(T.sum[x]==all) puts("YES");
else puts("NO");
}
}
return 0;
}