AT_abc344_e
问题陈述
给你一个长度为
请按照给出的顺序处理
1 x y:在A 中元素x 后面紧接着插入y 。当给出此查询时,保证x 存在于A 中。2 x:从A 中删除元素x 。当给出此查询时,保证x 存在于A 中。
保证在处理完每一个查询后,
处理完所有查询后,打印
思路
这道题为什么可以用链表做呢?因为这道题只需要最后输出整个序列,而不是实时查询。题目保证
所以链表写扩展性极低,所以比赛时写的分块。为什么某某部分人都写链表呢?不言而喻。但是又想到有个 rope 的玩意,于是很开心的写出了以下代码:
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include <ext/rope>
using namespace __gnu_cxx;
using namespace std;
const int N = 4e5 + 5;
int n, a[N];
rope<int> b;
signed main() {
scanf("%d", &n);
b.push_back(0);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b.push_back(a[i]);
}
int q;
scanf("%d",&q);
while (q--) {
int op, x, y;
scanf("%d%d",&op,&x);
if (op == 1) {
scanf("%d",&y);
b.insert(b.find(x)+1, y);
}
else {
b.erase(b.find(x), 1);
}
}
int t = b.size();
for (int i = 1; i < t; i++) printf("%d ", b.at(i));
}
这份代码只过了
于是又去写分块了。
而分块思路简单,代码好调好写,清晰明了,常数小,大部分链表代码跑不过分块。
考虑把序列分成 vector 动态维护,即把这个块的所有数都放到对应的 vector 里面。在某个元素后面插入一个数时,只需要在这个元素的对应块的 vector 中插入,由于每块的 vector 大小只有
但是,真的能保证每个 vector 都是 vector 的大小就达到了 vector 的大小变平均。
什么时候可以块重构?块重构是需要消耗
总的时间复杂度也是
注意事项:
-
题目不是在某个位置后面添加一个数,而是在某个元素后面添加一个数,所以需要开一个数组
bel_x 代表x 在序列的第几个块内。 -
本题值域较大,用
bel 数组需要离散化!用 map 复杂度就会变成O(n\sqrt n\log n) ,会 TLE!
// 394ms
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 5, M = 2000;
int n, q, a[N], lx[N], rx[N], len, b[N], tot;
int tmp[N], tl, sum[M], bel[N];
vector<int> bk[M];
// bk_i 存储这个块内的数
// lx_i,rx_i存储这个块的左右端点
// bel存储这个数所在的块
struct edge {
int op, l, r;
}ed[N];
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[++tot] = a[i];
int sq = sqrt(n);
len = n / sq;
for (int i = 1; i <= n / sq; i++) lx[i] = (i - 1) * sq + 1, rx[i] = lx[i] + sq - 1;
if (n % sq) {
len++;
lx[len] = rx[len - 1] + 1;
rx[len] = n;
}
int cnt = 0;
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
scanf("%d", &ed[i].op);
if (ed[i].op == 1) scanf("%d%d", &ed[i].l, &ed[i].r), b[++tot] = ed[i].l, b[++tot] = ed[i].r;
else scanf("%d", &ed[i].l);
}
sort(b + 1, b + tot + 1);
tot = unique(b + 1, b + tot + 1) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
for (int i = 1; i <= q; i++) {
if (ed[i].op == 1) ed[i].r = lower_bound(b + 1, b + tot + 1, ed[i].r) - b;
ed[i].l = lower_bound(b + 1, b + tot + 1, ed[i].l) - b;
}
for (int i = 1; i <= len; i++) {
for (int j = lx[i]; j <= rx[i]; j++) bk[i].push_back(a[j]), bel[a[j]] = i;
}
for (int cas = 1; cas <= q; cas++) {
int op = 0, l = 0, r = 0;
op = ed[cas].op;
cnt++;
if (op == 1) {
l = ed[cas].l, r = ed[cas].r;
int L = bel[l], lenb = bk[L].size(); // L是l所在的块
for (int i = 0; i < lenb; i++) {
if (bk[L][i] == l) {
l = i; // 扫描到l的位置
break;
}
}
if (l == lenb - 1) bk[L].push_back(r);
else bk[L].insert(bk[L].begin() + l + 1, r);
bel[r] = L;
rx[L]++;
for (int i = L + 1; i <= len; i++) lx[i]++, rx[i]++;
}
if (op == 2) {
l = ed[cas].l;
int L = bel[l], lenb = bk[L].size();
for (int i = 0; i < lenb; i++) {
if (bk[L][i] == l) {
l = i;
break;
}
}
bel[bk[L][l]] = 0;
bk[L].erase(bk[L].begin() + l);
rx[L]--;
for (int i = L + 1; i <= len; i++) lx[i]--, rx[i]--;
}
if (cnt >= 5 * sq) { // 一定次数后块重构
tl = 0;
for (int i = 1; i <= len; i++) {
for (auto j : bk[i]) tmp[++tl] = j;
}
sq = sqrt(tl);
len = tl / sq;
for (int i = 1; i <= tl / sq; i++) {
lx[i] = (i - 1) * sq + 1, rx[i] = lx[i] + sq - 1;
}
if (tl % sq) {
len++;
lx[len] = rx[len - 1] + 1;
rx[len] = tl;
}
for (int i = 1; i <= len; i++) {
bk[i].clear();
for (int j = lx[i]; j <= rx[i]; j++) bk[i].push_back(tmp[j]), bel[tmp[j]] = i;
}
cnt = 0;
}
}
for (int i = 1; i <= len; i++) {
if (bk[i].size() == 0) continue;
for (auto v : bk[i]) printf("%d ", b[v]);
}
}