序
感谢@hzwer大佬出的练习题
题目链接LOJ
本蒟过弱,实在不知道怎么压缩代码量了->_->
数列分块入门 1
给出一个长为 n n n 的数列,以及 n n n 个操作,操作涉及区间加法,单点查值。
将 n n n个数,按照每 n \sqrt{n} n为一个块标记.
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
每次对所给的
[
l
,
r
]
[l,r]
[l,r]区间进行讨论,分为"单蹦"和"块",对于不满块的数,直接暴力修改.
对于满足块的数,直接打一个标记,当访问的时候再进行修改即可,类似于线段树
l
a
z
y
lazy
lazy.
/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/
const int MAXN = 1e5 + 10;
int belong[MAXN], tot = 1, a[MAXN], n, cnt;
struct block {
int lazy;
int start, end;
} p[MAXN];
int main() {
cin >> n;
cnt = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]); // cin >> a[i];
belong[i] = tot;
if (i % cnt == 1)
p[tot].start = i;
if (i % cnt == 0)
p[tot++].end = i;
}
if (n % cnt) {
p[tot].start = p[tot - 1].end + 1;
p[tot++].end = n;
}
for (int i = 1; i <= n; i++) {
int opt, l, r, c;
scanf("%d %d %d %d", &opt, &l, &r, &c); // cin >> opt>> l >> r >> c;
if (opt)
cout << p[belong[r]].lazy + a[r] << endl;
else {
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
p[belong[l]].lazy += c;
else
for (int j = l; j <= p[belong[l]].end; j++)
a[j] += c;
if (p[belong[r]].end == r)
p[belong[r]].lazy += c;
else
for (int j = p[belong[r]].start; j <= r; j++)
a[j] += c;
for (int j = belong[l] + 1; j < belong[r]; j++)
p[j].lazy += c;
} else if (p[belong[l]].start == l && p[belong[r]].end == r)
p[belong[l]].lazy += c;
else
for (int j = l; j <= r; j++)
a[j] += c;
}
}
return 0;
}
数列分块入门 2
给出一个长为 n n n 的数列,以及 n n n 个操作,操作涉及区间加法,询问区间内小于某个值 x x x 的元素个数。
区间加法仿照
1
1
1即可.
对于每一个块内的数据,为了方便查询,我们分便对每一个块内的数据进行排序.
对于不满足块的数据,我们暴力处理,再将这个块内的数据排序,
满足块的区间,我们依然是打标记即可.
查询的时候,二分查找即可.
const int MAXN = 1e5 + 10;
/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/
int belong[MAXN], tot = 1, n, cnt, a[MAXN],b[MAXN];
struct block {
int lazy;
int start, end;
} p[MAXN];
void rechange(int l, int r)
{
for(int i = l; i <= r; i++)
b[i] = a[i];
sort(b + l, b + r + 1);
}
int lowerbound(int *array, int size, int key, int lazy) {
int first = 0, middle;
int half, len;
len = size;
while (len > 0) {
half = len >> 1;
middle = first + half;
if (array[middle] + lazy < key) {
first = middle + 1;
len = len - half - 1; //在右边子序列中查找
}
else
len = half; //在左边子序列(包含middle)中查找
}
return first;
}
int main() {
cin >> n;
cnt = sqrt(n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i]=a[i];
belong[i] = tot;
if (i % cnt == 1)
p[tot].start = i;
if (i % cnt == 0)
p[tot++].end = i;
}
if (n % cnt) {
p[tot].start = p[tot - 1].end + 1;
p[tot++].end = n;
}
for (int i = 1; i < tot; i++)
sort(b + p[i].start, b + p[i].end + 1);
for (int i = 1; i <= n; i++) {
int opt, l, r, c;
scanf("%d %d %d %d", &opt, &l, &r, &c);
if (opt) {
int ans = 0;
c *= c;
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
ans += lowerbound(b + l, p[belong[l]].end - l + 1, c, p[belong[l]].lazy);
else
for (int j = l; j <= p[belong[l]].end; j++)
if (a[j] + p[belong[j]].lazy < c)
ans++;
if (p[belong[r]].end == r)
ans += lowerbound(b + p[belong[r]].start, r - p[belong[r]].start + 1, c, p[belong[r]].lazy);
else
for (int j = p[belong[r]].start; j <= r; j++)
if (a[j] + p[belong[j]].lazy < c)
ans++;
for (int j = belong[l] + 1; j < belong[r]; j++)
ans += lowerbound(b + p[j].start, p[j].end - p[j].start + 1, c, p[j].lazy);
}
else if (p[belong[l]].start == l && p[belong[r]].end == r)
ans += lowerbound(b + l, r - l + 1, c, p[belong[l]].lazy);
else
for (int j = l; j <= r; j++)
if (a[j] + p[belong[j]].lazy < c)
ans++;
cout << ans << '\n';
}
else
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
p[belong[l]].lazy += c;
else {
for (int j = l; j <= p[belong[l]].end; j++)
a[j] += c;
rechange(p[belong[l]].start, p[belong[l]].end);
}
if (p[belong[r]].end == r)
p[belong[r]].lazy += c;
else {
for (int j = p[belong[r]].start; j <= r; j++)
a[j] += c;
rechange(p[belong[r]].start, p[belong[r]].end);
}
for (int j = belong[l] + 1; j < belong[r]; j++)
p[j].lazy += c;
}
else if (p[belong[l]].start == l && p[belong[r]].end == r)
p[belong[l]].lazy += c;
else {
for (int j = l; j <= r; j++)
a[j] += c;
rechange(p[belong[l]].start, p[belong[l]].end);
}
}
return 0;
}
数列分块入门 3
给出一个长为 n n n 的数列,以及 n n n 个操作,操作涉及区间加法,询问区间内小于某个值 x x x 的前驱(比其小的最大元素)。
和 2 2 2类似.
出题人的想法:
可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。
const int MAXN = 1e5 + 10;
/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/
int belong[MAXN], tot = 1, n, cnt, a[MAXN], b[MAXN];
struct block {
int lazy;
int start, end;
} p[MAXN];
void rechange(int l, int r)
{
for (int i = l; i <= r; i++)
b[i] = a[i];
sort(b + l, b + r + 1);
}
int lowerbound(int *array, int size, int key, int lazy) {
int first = 0, middle;
int half, len;
len = size;
while (len > 0) {
half = len >> 1;
middle = first + half;
if (array[middle] + lazy < key) {
first = middle + 1;
len = len - half - 1; //在右边子序列中查找
}
else
len = half; //在左边子序列(包含middle)中查找
}
return first;
}
int main() {
cin >> n;
cnt = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i]; //cin >> a[i];//scanf("%d", &a[i]);
belong[i] = tot;
if (i % cnt == 1)
p[tot].start = i;
if (i % cnt == 0)
p[tot++].end = i;
}
if (n % cnt) {
p[tot].start = p[tot - 1].end + 1;
p[tot++].end = n;
}
for (int i = 1; i < tot; i++)
sort(b + p[i].start, b + p[i].end + 1);
for (int i = 1; i <= n; i++) {
int opt, l, r, c;
cin >> opt >> l >> r >> c;// scanf("%d %d %d %d", &opt, &l, &r, &c);
if (opt) {
int ans = -1;
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l) {
int t = lowerbound(b + l, p[belong[l]].end - l + 1, c, p[belong[l]].lazy);
if (t)
ans = max(ans, b[p[belong[l]].start+t-1] + p[belong[l]].lazy);
}
else
for (int j = l; j <= p[belong[l]].end; j++)
if (a[j] + p[belong[j]].lazy < c)
ans = max(ans, a[j] + p[belong[j]].lazy);
if (p[belong[r]].end == r) {
int t = lowerbound(b + p[belong[r]].start, r - p[belong[r]].start + 1, c, p[belong[r]].lazy);
if (t)
ans = max(ans, b[p[belong[r]].start + t - 1] + p[belong[r]].lazy);
}
else
for (int j = p[belong[r]].start; j <= r; j++)
if (a[j] + p[belong[j]].lazy < c)
ans = max(ans, a[j] + p[belong[j]].lazy);
for (int j = belong[l] + 1; j < belong[r]; j++) {
int t = lowerbound(b + p[j].start, p[j].end- p[j].start + 1, c, p[j].lazy);
if (t)
ans = max(ans, b[p[j].start + t - 1] + p[j].lazy);
}
}
else if (p[belong[l]].start == l && p[belong[r]].end == r) {
int t = lowerbound(b + l, r - l + 1, c, p[belong[l]].lazy);
if (t)
ans = max(ans, b[l + t - 1] + p[belong[l]].lazy);
}
else
for (int j = l; j <= r; j++)
if (a[j] + p[belong[j]].lazy < c)
ans = max(ans, a[j] + p[belong[j]].lazy);
cout << ans << '\n';
}
else
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
p[belong[l]].lazy += c;
else {
for (int j = l; j <= p[belong[l]].end; j++)
a[j] += c;
rechange(p[belong[l]].start, p[belong[l]].end);
}
if (p[belong[r]].end == r)
p[belong[r]].lazy += c;
else {
for (int j = p[belong[r]].start; j <= r; j++)
a[j] += c;
rechange(p[belong[r]].start, p[belong[r]].end);
}
for (int j = belong[l] + 1; j < belong[r]; j++)
p[j].lazy += c;
}
else if (p[belong[l]].start == l && p[belong[r]].end == r)
p[belong[l]].lazy += c;
else {
for (int j = l; j <= r; j++)
a[j] += c;
rechange(p[belong[l]].start, p[belong[l]].end);
}
}
return 0;
}
数列分块入门 4
给出一个长为 n n n 的数列,以及 n n n 个操作,操作涉及区间加法,区间求和。
求和预处理一下就阔以了,打标记的时候是长度 ∗ * ∗加法
typedef long long ll;
const int MAXN = 1e5 + 10;
/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x];
*/
ll cnt, a[MAXN];
int belong[MAXN], tot = 1, n;
struct block {
ll lazy;
ll sum;
int start, end;
} p[MAXN];
int main() {
cin >> n;
cnt = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> a[i]; //scanf("%d", &a[i]);
belong[i] = tot;
p[tot].sum += a[i];
if (i % cnt == 1)
p[tot].start = i;
if (i % cnt == 0)
p[tot++].end = i;
}
if (n % cnt) {
p[tot].start = p[tot - 1].end + 1;
p[tot++].end = n;
}
for (int i = 1; i <= n; i++) {
int opt, l, r;
ll c;
cin >> opt >> l >> r >> c;// scanf("%d %d %d %d", &opt, &l, &r, &c);
if (opt) {
ll ans = 0;
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
ans = (ans + (p[belong[l]].lazy*(p[belong[l]].end - p[belong[l]].start + 1)) % (c + 1) + p[belong[l]].sum % (c + 1)) % (c + 1);
else
for (int j = l; j <= p[belong[l]].end; j++)
ans = (ans + a[j] % (c + 1) + p[belong[j]].lazy % (c + 1)) % (c + 1);
if (p[belong[r]].end == r)
ans = (ans + (p[belong[r]].lazy*(p[belong[r]].end - p[belong[r]].start + 1)) % (c + 1) + p[belong[r]].sum % (c + 1)) % (c + 1);
else
for (int j = p[belong[r]].start; j <= r; j++)
ans = (ans + a[j] % (c + 1) + p[belong[j]].lazy % (c + 1)) % (c + 1);
for (int j = belong[l] + 1; j < belong[r]; j++)
ans = (ans + (p[j].lazy*(p[j].end - p[j].start + 1)) % (c + 1) + p[j].sum % (c + 1)) % (c + 1);
}
else if (p[belong[l]].start == l && p[belong[r]].end == r)
ans = (ans + (p[belong[l]].lazy*(p[belong[l]].end - p[belong[l]].start + 1)) % (c + 1) + p[belong[l]].sum % (c + 1)) % (c + 1);
else
for (int j = l; j <= r; j++)
ans = (ans + a[j] % (c + 1) + p[belong[j]].lazy % (c + 1)) % (c + 1);
cout << ans << '\n';
}
else
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
p[belong[l]].lazy += c;
else
for (int j = l; j <= p[belong[l]].end; j++) {
a[j] += c;
p[belong[l]].sum += c;
}
if (p[belong[r]].end == r)
p[belong[r]].lazy += c;
else
for (int j = p[belong[r]].start; j <= r; j++) {
p[belong[r]].sum += c;
a[j] += c;
}
for (int j = belong[l] + 1; j < belong[r]; j++)
p[j].lazy += c;
}
else if (p[belong[l]].start == l && p[belong[r]].end == r)
p[belong[l]].lazy += c;
else
for (int j = l; j <= r; j++) {
a[j] += c;
p[belong[l]].sum += c;
}
}
return 0;
}
数列分块入门 5
给出一个长为 n n n 的数列 ,以及 n n n 个操作,操作涉及区间开方,区间求和。
这个题目其实比较搞人==
对于一个数,其属于
{
−
2
31
,
2
31
−
1
}
\{-2^{31},2^{31}-1\}
{−231,231−1},最多开方不超过
4
4
4次.
还是和之前一样,单个暴力,整块标记.
对于一个块,如果开方次数超过
4
4
4次,或者整个块只有
1
1
1或
0
0
0,我们就可以认为不需要对其处理了,只记下和即可.
自己代码实现的时候,注意细节.
typedef long long ll;
const int MAXN = 1e5 + 10;
/*
belong[x]:元素x所在的块的编号,样例代码中为bl[x];
start[x]:编号为x的块的最左边的点,样例代码中为st[x];
end[x]:编号为x的块的最右边的点,样例代码中为ed[x]; */
int cnt, a[MAXN];
int belong[MAXN], tot = 1, n;
struct block {
int lazy;
ll sum;
int start, end;
bool f;
block() {
lazy = start = end = sum = 0;
f = false;
}} p[MAXN];
void built() {
cin >> n;
cnt = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> a[i]; // scanf("%d", &a[i]);
belong[i] = tot;
if (a[i] != 0)
p[tot].sum++;
if (i % cnt == 1)
p[tot].start = i;
if (i % cnt == 0)
p[tot++].end = i;
}
if (n % cnt) {
p[tot].start = p[tot - 1].end + 1;
p[tot++].end = n;
}
}
void print(int l, int r) {
ll ans = 0, c;
cin >> c;
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
if (p[belong[l]].lazy > 4 || p[belong[l]].f)
ans += p[belong[l]].sum;
else {
p[belong[l]].f = true;
for (int i = p[belong[l]].start; i <= p[belong[l]].end; i++) {
int lazy = p[belong[l]].lazy, x = a[i];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
if (x != 1 && x != 0)
p[belong[l]].f = false;
ans += x;
}
} else
for (int i = l; i <= p[belong[l]].end; i++) {
int lazy = p[belong[l]].lazy, x = a[i];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
ans += x;
}
if (p[belong[r]].end == r)
if (p[belong[r]].lazy > 4 || p[belong[r]].f)
ans += p[belong[r]].sum;
else {
p[belong[r]].f = true;
for (int i = p[belong[r]].start; i <= p[belong[r]].end; i++) {
int lazy = p[belong[r]].lazy, x = a[i];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
if (x != 1 && x != 0)
p[belong[r]].f = false;
ans += x;
}
} else
for (int i = p[belong[r]].start; i <= r; i++) {
int lazy = p[belong[r]].lazy, x = a[i];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
ans += x;
}
for (int i = belong[l] + 1; i < belong[r]; i++)
if (p[i].lazy > 4 || p[i].f)
ans += p[i].sum;
else {
p[i].f = true;
for (int j = p[i].start; j <= p[i].end; j++) {
int lazy = p[i].lazy, x = a[j];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
if (x != 1 && x != 0)
p[i].f = false;
ans += x;
}
}
}
else if (p[belong[l]].start == l && p[belong[r]].end == r)
if (p[belong[l]].lazy > 4 || p[belong[l]].f)
ans += p[belong[l]].sum;
else {
p[belong[l]].f = true;
for (int i = p[belong[l]].start; i <= p[belong[l]].end; i++) {
int lazy = p[belong[r]].lazy, x = a[i];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
if (x != 1 && x != 0)
p[belong[l]].f = false;
ans += x;
}
}
else
for (int i = l; i <= r; i++) {
int lazy = p[belong[l]].lazy, x = a[i];
while (lazy) {
if (x == 0 || x == 1)
break;
x = sqrt(x);
lazy--;
}
ans += x;
}
cout << ans << '\n';
}
void update(int l, int r) {
ll c;
cin >> c;
if (belong[l] != belong[r]) {
if (p[belong[l]].start == l)
p[belong[l]].lazy++;
else
for (int i = l; i <= p[belong[l]].end; i++)
a[i] = sqrt(a[i]);
if (p[belong[r]].end == r)
p[belong[r]].lazy++;
else
for (int i = p[belong[r]].start; i <= r; i++)
a[i] = sqrt(a[i]);
for (int i = belong[l] + 1; i < belong[r]; i++)
p[i].lazy++;
} else if (p[belong[l]].start == l && p[belong[r]].end == r)
p[belong[l]].lazy++;
else
for (int i = l; i <= r; i++)
a[i] = sqrt(a[i]);
}
int main() {
built();
for (int i = 1; i <= n; i++) {
int opt, l, r;
cin >> opt >> l >> r; // scanf("%d %d %d %d",
// &opt,
// &l, &r, &c);
if (opt)
print(l, r);
else
update(l, r);
}
return 0;
}
数列分块入门 6
给出一个长为 4n$ 的数列,以及 n n n 个操作,操作涉及单点插入,单点询问,数据随机生成.
到了喜闻乐见的动态分块了
23333
23333
23333.
c
+
+
c++
c++的
v
e
c
t
o
r
vector
vector大法好,我是不会用指针写链表的,拒绝
每次插入一个数,就找到对应的块,扔进去就行.
将插入的次数记下来,当次数超过
n
\sqrt n
n的时候就进行重构,也就是重新分块.
然后就没有然后了.
const int MAXN = 1e6 + 10;
const int INF = 1e8 + 10;
const int MOD = 998244353;
const int ans = 11;
typedef long long ll;
int a[MAXN];
vector<int>p[MAXN];
int n, tot = 0, m, optt;
void find(int k)
{
int num = 0;
for (int i = 0; i < tot; i++) {
num += p[i].size();
if (num >= k) {
num -= p[i].size();
k = k - num - 1;
cout << p[i][k] << '\n';
break;
}
}
}
void rebuild()
{
int num = 0;
for (int i = 0; i < tot; i++) {
for (int j = 0; j < p[i].size(); j++)
a[++num] = p[i][j];
p[i].clear();
}
n = num, m = sqrt(n), tot = 0;
for (int i = 1; i <= n; i++) {
p[tot].push_back(a[i]);
if (i%m == 0)
tot++;
}
if (n%m != 0)
tot++;
}
void insert(int k, int x)
{
int num = 0;
for (int i = 0; i < tot; i++) {
num += p[i].size();
if (num >= k) {
num -= p[i].size();
k = k - num - 1;
p[i].insert(p[i].begin() + k, x);
optt++;
break;
}
}
if (optt == m) {
rebuild();
optt = 0;
}
}
int main()
{
cin >> n;
m = sqrt(n);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d",&x);
p[tot].push_back(x);
if (i%m == 0)
tot++;
}
if (n%m != 0)
tot++;
int opt, l, r, c, q = n;
for (int i = 1; i <= q; i++) {
scanf("%d%d%d%d",&opt,&l,&r,&c);
if (opt)
find(r);
else
insert(l, r);
}
return 0;
}