CF2115F1 Gellyfish and Lycoris Radiata (Easy Version) 题解

Description

Gellyfish 有一个包含 nn 个集合的数组。最初,所有集合都是空的。

现在,Gellyfish 将进行 qq 次操作。每次操作包含一次修改操作和一次查询操作,对于第 ii 次(1iq1 \leq i \leq q)操作:

首先,会有一次修改操作,可能是以下三种之一:

  1. 插入操作:给定一个整数 rr。将元素 ii 插入到第 11 到第 rr 个集合中。注意,这里插入的元素是 ii,即操作的编号,而不是集合的编号。
  2. 反转操作:给定一个整数 rr。将第 11 到第 rr 个集合的顺序反转。
  3. 删除操作:给定一个整数 xx。从所有包含 xx 的集合中删除元素 xx

然后进行一次查询操作:

  • 查询操作:给定一个整数 pp。输出第 pp 个集合中的最小元素(如果该集合为空,则答案为 00)。

现在,Flower 需要为每次查询操作提供答案。请你帮助她!

本题有一个额外的约束:Gellyfish 只有在 Flower 回答了上一次查询操作后,才会给出下一次操作。也就是说,你需要在线处理本题。具体请参考输入格式。

n,q105n,q\leq 10^5

Solution

首先如果没有删除操作是好做的,可以直接维护。然后会发现这个删除操作非常麻烦,因为我们根本不知道需要修改哪些集合里的元素。

但是注意到做 kk 次操作会把整个序列分成 O(k)O(k) 个连续段,每个连续段满足在操作之前是个区间,且现在的顺序是原先的顺序或者倒序的形式。这启发我们询问分块。

具体地,对每 BB 次修改进行分块,块内先假定每个集合都没有元素,然后修改时维护连续段,由于只有 O(B)O(B) 个连续段,加入元素时暴力即可。

对于删除操作,块内的直接打标记,不在块内的,就找到其对应的块,把其从所在的所有集合删掉。

对于询问,块内的贡献可以直接求,块外的贡献还需要知道当前位置在之前任意块的最终位置,重构时可以预处理,得到这个之后也是直接求。

需要精细实现,实测能过 Hard Version。

时间复杂度:O((n+q)q)O((n+q)\sqrt q),空间复杂度:O((n+q)q)O((n+q)\sqrt q)

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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 3e5 + 5, kMaxB = 550;

struct Node {
int l, r;
Node(int _l = 0, int _r = 0) : l(_l), r(_r) {}
};

struct Block {
int sz;
Node seg[kMaxB];
std::vector<short> vv[kMaxB];
int cnt[kMaxB];
void split(int x) {
bool fl = 1;
int ps = 0;
for (int i = 1, now = 0; i <= sz; ++i) {
auto [l, r] = seg[i];
now += abs(r - l) + 1;
if (now == x) { fl = 0; break; }
if (now > x && !ps) { ps = i; break; }
}
if (!fl) return;
for (int i = sz; i > ps; --i) {
std::swap(seg[i], seg[i + 1]);
vv[i].swap(vv[i + 1]);
}
vv[ps + 1] = vv[ps];
int now = 0;
for (int i = 1; i < ps; ++i) {
auto [l, r] = seg[i];
now += abs(r - l) + 1;
}
assert(now < x);
auto [l, r] = seg[ps];
if (l <= r) {
seg[ps] = {l, l + x - now - 1};
seg[ps + 1] = {l + x - now, r};
} else {
seg[ps] = {l, l - (x - now) + 1};
seg[ps + 1] = {l - (x - now), r};
}
++sz;
}
int getbel(int x) {
for (int i = 1, now = 0; i <= sz; ++i) {
auto [l, r] = seg[i];
now += abs(r - l) + 1;
if (now >= x) return i;
}
assert(0);
}
int getpre(int x) {
// std::cerr << "heige\n";
for (int i = 1, now = 0; i <= sz; ++i) {
auto [l, r] = seg[i];
int lst = now;
now += abs(r - l) + 1;
// std::cerr << "??? " << l << ' ' << r << '\n';
if (now >= x) {
if (l <= r) return l + x - lst - 1;
else return l - (x - lst) + 1;
}
}
assert(0);
return 0;
}
} t[kMaxB];

int n, q, B;
int op[kMaxN], a[kMaxN], p[kMaxN], res[kMaxN];
short pos[kMaxB][kMaxN];
int fir[kMaxN]; // pos[i][j] : 第 i 段末尾时,初始 j 所在的块;fir[i][j] : 第 i 末尾时,在第 j 个的数的初始位置
bool del[kMaxN];
std::vector<short> pp[kMaxN];

void update1(int id, int x) { // 给 [1, x] 插入 id
int bid = (id - 1) / B + 1;
t[bid].split(x);
int now = 0;
// std::cerr << x << " : \n";
// for (int i = 1; i <= t[bid].sz; ++i) std::cerr << t[bid].seg[i].l << ' ' << t[bid].seg[i].r << '\n';
for (int i = 1; i <= t[bid].sz; ++i) {
auto [l, r] = t[bid].seg[i];
now += abs(r - l) + 1;
// std::cerr << now << '\n';
if (now >= x) assert(now == x);
if (now <= x) t[bid].vv[i].emplace_back(id - (bid - 1) * B);
if (now >= x) break;
}
// std::cerr << "--------------\n";
}

void update2(int id, int x) { // [1, x] 翻转
int bid = (id - 1) / B + 1;
t[bid].split(x);
int ps = 0;
for (int i = 1, now = 0; i <= t[bid].sz; ++i) {
auto [l, r] = t[bid].seg[i];
now += abs(r - l) + 1;
if (now == x) ps = i;
}
assert(ps);
for (int i = 1, j = ps; i < j; ++i, --j) {
std::swap(t[bid].seg[i], t[bid].seg[j]);
t[bid].vv[i].swap(t[bid].vv[j]);
}
for (int i = 1; i <= ps; ++i) std::swap(t[bid].seg[i].l, t[bid].seg[i].r);
}

void update3(int id, int x) { // 把 x 删掉
if (x > id || del[x]) return;
assert(!del[x]);
del[x] = 1;
if ((x - 1) / B + 1 < (id - 1) / B + 1) {
int bid = (x - 1) / B + 1;
for (auto i : pp[x]) {
if (!--t[bid].cnt[i]) std::vector<short>().swap(t[bid].vv[i]);
}
std::vector<short>().swap(pp[x]);
}
}

void rebuild(int bid) {
static int lst[kMaxN];
for (int i = 1; i <= n; ++i) lst[i] = fir[i];
int cnt = 0;
// std::cerr << "????????????\n";
for (int i = 1; i <= t[bid].sz; ++i) {
auto [l, r] = t[bid].seg[i];
// std::cerr << l << ' ' << r << '\n';
if (l <= r) {
for (int j = l; j <= r; ++j) pos[bid][lst[j]] = i, fir[++cnt] = lst[j];
} else {
for (int j = l; j >= r; --j) pos[bid][lst[j]] = i, fir[++cnt] = lst[j];
}
assert(!t[bid].cnt[i]);
t[bid].cnt[i] = 0;
for (auto _v : t[bid].vv[i]) {
int v = _v + (bid - 1) * B;
if (!del[v]) {
++t[bid].cnt[i], pp[v].emplace_back(i);
}
}
}
assert(cnt == n);
// if (bid == 1) {
// for (auto x : t[bid].vv[2]) std::cerr << x << ' ';
// std::cerr << '\n';
// }
// std::cerr << "????????????\n";
}

int query(int id, int x) {
int bid = (id - 1) / B + 1;
int ps = t[bid].getbel(x);
int ret = 1e9;
for (auto _v : t[bid].vv[ps]) {
int v = _v + (bid - 1) * B;
if (!del[v])
ret = std::min(ret, v);
}
if (bid > 1) {
int ff = fir[t[bid].getpre(x)];
for (int i = 1; i <= bid - 1; ++i) {
int bb = pos[i][ff];
if (t[i].cnt[bb]) {
for (auto _v : t[i].vv[bb]) {
int v = _v + (i - 1) * B;
if (!del[v]) ret = std::min(ret, v);
}
return ret;
}
// int cc = 0;
// // std::cerr << id << ' ' << i << '\n';
// for (auto v : t[i].vv[bb])
// if (!del[v]) cc += !del[v], ret = std::min(ret, v);
// assert(cc == t[i].cnt[bb]);
// if (t[i].cnt[bb]) {
// for (auto v : t[i].vv[bb])
// if (!del[v]) ret = std::min(ret, v);
// assert(ret != 1e9);
// return ret;
// }
}
}
if (ret == 1e9) ret = 0;
return ret;
}

void dickdreamer() {
std::cin >> n >> q;
B = sqrtl(q);
if (!B) B = 1;
for (int i = 1; i <= n; ++i) fir[i] = i;
for (int i = 1; i <= q; ++i) {
// for (int i = 1; i <= 8; ++i) {
if ((i - 1) % B == 0) {
int bid = (i - 1) / B + 1;
t[bid].sz = 1, t[bid].seg[1] = {1, n};
}
std::cin >> op[i] >> a[i] >> p[i];
a[i] = (a[i] + res[i - 1] - 1) % (op[i] <= 2 ? n : q) + 1;
p[i] = (p[i] + res[i - 1] - 1) % n + 1;
// std::cerr << op[i] << ' ' << a[i] << ' ' << p[i] << '\n';
// if (i == 4) std::cerr << "fuckkk " << pos[1][2] << '\n';
if (op[i] == 1) update1(i, a[i]);
else if (op[i] == 2) update2(i, a[i]);
else update3(i, a[i]);
res[i] = query(i, p[i]);
std::cout << res[i] << '\n';
if (i % B == 0) rebuild(i / B);
// std::cerr << "??? " << i << ' ' << res[i] << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF2115F1 Gellyfish and Lycoris Radiata (Easy Version) 题解
https://sobaliuziao.github.io/2025/07/23/post/d7043d4d.html
作者
Egg_laying_master
发布于
2025年7月23日
许可协议