Luckyleaves's Blog

stay hungry,stay foolish greedy and lucky

ZJOI2016 大森林

orz优先级。

一道比较神秘的题目。

首先我们发现在线的做法只能做到暴力的$mn\log n$,这种报废的做法显然是不可取的,我们考虑离线。

首先考虑第一种操作怎么快速维护:为了避免建重复的节点,我们应该将所有的树压成一棵树(多的节点断掉即可)。

思考为什么复杂度如此的高,最后搞来搞去就发现第二种操作是最不可做的,我们应该怎样才能将子树信息进行移动,这其实考了虚点的概念,就是我们更换生长节点的时候,建立一个虚点,将后来子树的信息全部加在虚点上,之后到了撤销生长节点的时候,将虚点直接挪过去即可。

紧接着就不难得出询问的实现方法,设一个节点到根的路径上的的非虚节点个数为$sz[x]$,则答案为:$sz[x]+sz[y]-2*lca(x,y)$。

因为不能改变树的结构,所以不能$split$。

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
155
156
157
158
#include<bits/stdc++.h>
#define PII pair<int, int>
using namespace std;
template <class T>
inline void read(T &res)
{
res = 0; bool flag = 0;
char c = getchar();
while('0' > c || c > '9') { if(c == '-') flag = 1; c = getchar();}
while('0' <= c && c <= '9') res = (res << 3) + (res << 1) + (c ^ 48), c = getchar();
if(flag) res = -res;
}
template <class T, class ...Arg>
inline void read(T &res, Arg &...com){ read(res), read(com...);}
template <class T>
void out(T res)
{
if(res > 9) out(res / 10);
putchar(res % 10 + '0');
}
template <class T>
inline void write(T res)
{
if(res < 0) putchar('-'), res = -res;
out(res), putchar('\n');
}
const int N = 4e5 + 5;
int n, m;
struct Node{
int s[2], p, sum, v;
#define l(x) tr[x].s[0]
#define r(x) tr[x].s[1]
#define fa(x) tr[x].p
}tr[N];
inline bool isroot(int x)
{
return l(fa(x)) != x && r(fa(x)) != x;
}
inline void pushup(int x)
{
tr[x].sum = tr[l(x)].sum + tr[r(x)].sum + tr[x].v;
}
inline void rotate(int x)
{
int y = fa(x), z = fa(y);
int k = tr[y].s[1] == x;
if(!isroot(y)) tr[z].s[tr[z].s[1] == y] = x;
fa(x) = z;
tr[y].s[k] = tr[x].s[k ^ 1];
if(tr[x].s[k ^ 1]) fa(tr[x].s[k ^ 1]) = y;
tr[x].s[k ^ 1] = y, fa(y) = x;
pushup(y), pushup(x);
}
int st[N];
inline void splay(int x)
{
while(!isroot(x))
{
int y = fa(x), z = fa(y);
if(!isroot(y))
{
if((r(y) == x) ^ (r(z) == y)) rotate(x);
else rotate(y);
}
rotate(x);
}
}
inline int access(int x)
{
int y = 0;
for(; x; y = x, x = fa(x)) splay(x), r(x) = y, pushup(x);
return y;
}
inline void link(int x, int y)
{
splay(x), fa(x) = y;
}
inline void cut(int x)
{
access(x), splay(x);
fa(l(x)) = 0;
l(x) = 0;
pushup(x);
}
int cnt, idx, last, tot, num;
int L[N], R[N], id[N]; // 每个点的出现坐标,删除坐标,下标
inline void make(int v)
{
cnt ++, tr[cnt].sum = tr[cnt].v = v;
}
struct Ask{
int pos, id, x, y;
bool operator< (Ask b)
{
return pos == b.pos ? id < b.id : pos < b.pos;
}
}q[N];
inline int query(int x, int y)
{
int ans = 0, lca;
access(x), splay(x), ans += tr[x].sum;
lca = access(y), splay(y), ans += tr[y].sum;
access(lca), splay(lca), ans -= 2 * tr[lca].sum;
return ans;
}
int ans[N];
int main()
{
read(n, m);
make(1);
L[1] = 1, R[1] = n;
id[++ idx] = 1;
make(0);
link(2, 1);
last = 2;
int op, l, r, x, u, v;
for(int i = 1;i <= m;i ++)
{
read(op);
if(op == 0)
{
read(l, r);
make(1);
L[++ idx] = l, R[idx] = r, id[idx] = cnt;
q[++ tot] = {1, i - m, cnt, last};
}
if(op == 1)
{
read(l, r, x);
l = max(L[x], l), r = min(r, R[x]);
if(l <= r)
{
make(0);
link(cnt, last);
q[++ tot] = {l, i - m, cnt, id[x]};
q[++ tot] = {r + 1, i - m, cnt, last};
last = cnt;
}
}
if(op == 2)
{
read(x, u, v);
q[++ tot] = {x, ++ num, id[u], id[v]};
}
}
sort(q + 1, q + tot + 1);
for(int i = 1, j = 1;i <= n;i ++)
for(;i == q[j].pos;j ++)
{
if(q[j].id <= 0) cut(q[j].x), link(q[j].x, q[j].y);
else
{
ans[q[j].id] = query(q[j].x, q[j].y);
}
}
for(int i = 1;i <= num;i ++) write(ans[i]);
return 0;
}