洛谷P3613 - 睡觉困难综合征

 题解 / 洛谷
被浏览

首先我们要知道这道题的原型是「NOI2014」起床困难综合症。

总是有出题人喜欢把序列上用线段树解决的题目出到树上,让选手强行写个树链剖分或树分治或某种动态树数据结构。

序列问题转化为树上问题,很容易想到树链剖分,直接上板子。

统计答案时因为二进制的特殊性质,后面每一位都选 1 都不大于这一位选 1,所以直接贪心即可。

注意到题面里询问的 x,y 是有方向性的,我们还要维护反向的状态。

但是如果每一位单独维护的话,时间复杂度爆炸,考虑把这些信息压进一个 unsigned long long 里。

这样的话怎么维护信息呢?我们定义一个结构体表示这段进来为 0/1 时出来的情况,不难发现如果这一位出来是 1 只有这么几种情况:左儿子进来是 0,出来是 0,右儿子进来是 0,出来是 1;左儿子进来是 1,出来是 0,右儿子进来是 0,出来是 1;左儿子进来是 0,出来是 1,右儿子进来是 1,出来是 1;左儿子进来是 1,出来是 1,右儿子进来是 1,出来是 1。

用 C++表示就是:

1
2
ans.x0 = (x0 & b.x1) | ((~x0) & b.x0);
ans.x1 = (x1 & b.x1) | ((~x1) & b.x0);

可见自然语言的繁琐

代码中用到了一个小技巧——重载运算符,这个能使代码更加简洁易懂,提升代码复用性。

以下就是冗长的代码了:

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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <cstdio>
#include <iostream>
using namespace std;
typedef unsigned long long ull;
#define gc c = getchar()
ull read() {
ull x = 0, f = 1;
char gc;
for (; !isdigit(c); gc)
if (c == '-') f = -1;
for (; isdigit(c); gc) x = x * 10 + c - '0';
return x * f;
}
#undef gc
const int N = 1e5 + 5;
int nedge, head[N];
struct Edge {
int to, nxt;
} edge[N << 1];
void add(int x, int y) {
edge[++nedge].to = y;
edge[nedge].nxt = head[x];
head[x] = nedge;
}
int n, m, k;
int dep[N], fa[N], son[N], siz[N];
void dfs1(int x, int f) {
fa[x] = f;
siz[x] = 1;
dep[x] = dep[f] + 1;
int mx = 0;
for (int i = head[x]; i; i = edge[i].nxt) {
int y = edge[i].to;
if (y == f) continue;
dfs1(y, x);
siz[x] += siz[y];
if (siz[y] > mx) {
mx = siz[y];
son[x] = y;
}
}
}
int tot, id[N], p[N], top[N];
void dfs2(int x, int topf) {
top[x] = topf;
id[x] = ++tot;
p[tot] = x;
if (!son[x]) return;
dfs2(son[x], topf);
for (int i = head[x]; i; i = edge[i].nxt) {
int y = edge[i].to;
if (y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
}
struct Data {
ull x0, x1;
Data() { x0 = 0, x1 = ~0; }
Data(int opt, ull x) {
x0 = 0, x1 = ~0;
if (opt == 1) {
x0 &= x;
x1 &= x;
}
if (opt == 2) {
x0 |= x;
x1 |= x;
}
if (opt == 3) {
x0 ^= x;
x1 ^= x;
}
}
Data operator+(const Data &b) const {
Data ans;
ans.x0 = (x0 & b.x1) | ((~x0) & b.x0);
ans.x1 = (x1 & b.x1) | ((~x1) & b.x0);
return ans;
}
ull query(ull x) {
ull ans = 0;
for (int i = k - 1; ~i; i--) {
if (x0 & (1ull << i)) {
ans += (1ull << i);
} else if (x1 & (1ull << i) && (1ull << i) <= x) {
x -= (1ull << i);
ans += (1ull << i);
}
}
return ans;
}
};
Data a[N];
#define ls (k << 1)
#define rs (k << 1 | 1)
struct Segtree {
Data lv[N << 2], rv[N << 2];
void pushup(int k) {
lv[k] = lv[ls] + lv[rs];
rv[k] = rv[rs] + rv[ls];
}
void build(int k, int l, int r) {
if (l == r) {
lv[k] = rv[k] = a[p[l]];
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(k);
}
void modify(int k, int l, int r, int p, Data x) {
if (l == r) return (void)(lv[k] = rv[k] = x);
int mid = (l + r) >> 1;
if (p <= mid) {
modify(ls, l, mid, p, x);
} else {
modify(rs, mid + 1, r, p, x);
}
pushup(k);
}
Data query(int k, int l, int r, int ql, int qr, bool opt) {
if (ql <= l && r <= qr) return opt ? rv[k] : lv[k];
int mid = (l + r) >> 1;
if (qr <= mid) return query(ls, l, mid, ql, qr, opt);
if (ql > mid) return query(rs, mid + 1, r, ql, qr, opt);
Data L = query(ls, l, mid, ql, qr, opt);
Data R = query(rs, mid + 1, r, ql, qr, opt);
if (opt) return R + L;
return L + R;
}
} seg;
#undef ls
#undef rs
Data query(int x, int y) {
Data L, R;
while (top[x] ^ top[y]) {
if (dep[top[x]] > dep[top[y]]) {
L = L + seg.query(1, 1, n, id[top[x]], id[x], 1);
x = fa[top[x]];
} else {
R = seg.query(1, 1, n, id[top[y]], id[y], 0) + R;
y = fa[top[y]];
}
}
Data tmp;
if (dep[x] > dep[y]) {
tmp = L + seg.query(1, 1, n, id[y], id[x], 1);
tmp = tmp + R;
return tmp;
}
if (dep[x] > dep[y]) return L + seg.query(1, 1, n, id[y], id[x], 1) + R;
return L + seg.query(1, 1, n, id[x], id[y], 0) + R;
}
int main() {
n = read(), m = read(), k = read();
for (int i = 1; i <= n; i++) {
int opt = read();
ull x = read();
a[i] = Data(opt, x);
}
for (int i = 1; i < n; i++) {
int x = read(), y = read();
add(x, y), add(y, x);
}
dfs1(1, 0), dfs2(1, 1);
seg.build(1, 1, n);
while (m--) {
int opt = read(), x = read(), y = read();
ull z = read();
if (opt == 2) {
seg.modify(1, 1, n, id[x], Data(y, z));
} else {
Data ans = query(x, y);
printf("%llu\n", ans.query(z));
}
}
}