题目链接:#6282. 数列分块入门 6
题目大意
给出一个长为 (n) 的数列,以及 (n) 个操作,操作涉及单点插入,单点询问
solution
我们对于这两种操作:
修改操作: 我们计入一下每个块的大小,然后找到所在的快, 每找到一个整块直接减去这个块的大小即可,然后当不能减了,就是所在块,直接暴力插入即可
查询操作: 同修改操作,找到所在块,然后直接输出
然后,我们喜闻乐见的 超时了
我们发现如果一直插入一个块里,这个块会变得非常大,我们就会超时
我们有两种应对措施:
-
我们可以每多 (sqrt(n)) 的时候,进行暴力重构
-
我们也可以在某个块达到一定大小时,暴力重构
暴力重构的时间复杂度是正确的,自己可以证明一下本人不想写了
Code:
/**
* Author: Alieme
* Data: 2020.9.8
* Problem: LibreOJ #6282
* Time: O()
*/
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define rr register
#define inf 1e9
#define MAXN 200010
using namespace std;
inline void read(int &T) {
T = 0;
int f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) T = T * 10 + (ch ^ 48), ch = getchar();
if (f) T = -T;
}
void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
int n, len, sum, tot;
int siz[MAXN], id[MAXN], a[MAXN];
int v[600][MAXN];
inline void build() {
sum = 0;
int cnt = 0;
for (rr int i = 1; i <= id[tot]; i++)
for (rr int j = 1; j <= siz[i]; j++)
a[++cnt] = v[i][j];
tot = cnt;
memset(siz, 0, sizeof siz);
for (rr int i = 1; i <= tot; i++)
id[i] = (i - 1) / len + 1, v[id[i]][++siz[id[i]]] = a[i];
}
inline void add(int l, int r) {
int i;
sum++;
for (i = 1; i <= id[tot]; i++)
if (l - siz[i] > 0) l -= siz[i];
else break;
for (rr int k = siz[i] + 1; k >= l; k--) v[i][k + 1] = v[i][k];
v[i][l] = r;
siz[i]++;
if (sum == len) build();
}
inline int query(int r) {
int i;
for (i = 1; i <= id[tot]; i++)
if (r - siz[i] > 0) r -= siz[i];
else break;
return v[i][r];
}
signed main() {
// freopen("a1.in", "r", stdin);
// freopen("a.out", "w", stdout);
read(n);
len = sqrt(n);
for (rr int i = 1; i <= n; i++) read(a[i]), id[i] = (i - 1) / len + 1, v[id[i]][++siz[id[i]]] = a[i];
tot = n;
for (rr int i = 1; i <= n; i++) {
int opt, l, r, c;
read(opt), read(l), read(r), read(c);
if (opt == 0) add(l, r);
if (opt == 1) cout << query(r) << "
";
}
}