前言
题目传送门 Luogu P2801 教主的魔法 。
好题,将分块的精髓------整块维护,局部暴力------体现得淋漓尽致。
题意
给定 \(n\) 个数,实现以下两种操作:
-
M L R W,表示将 \(L,R\) 内每个数加上 \(W\) 。 -
A L R C,表示询问 \(L,R\) 内有多少个数 \(\ge C\) 。
对于 \(100\%\) 的数据,\(N≤10^6\),\(Q≤3000\),\(1≤W≤1000\),\(1≤C≤10^9\)。
思路
首先考虑朴素的分块。容易想到以下方法:
-
读取数列 \(a_n\) ,分为 \(\sqrt{n}\) 块;
-
区间修改。我们维护增量标记
add[]来加速整块修改;碎块暴力修改。 -
区间查询。要查询区间内 \(\ge c\) 的数有多少,容易想到如下思路:对于整块我们直接二分,对于碎块暴力枚举。可是我们无法保证块内的数有序,如果每次查询都排序,复杂度还不如直接枚举。考虑找一种办法能让我们不用重复排序。
这时,优化方法也就显而易见了。注意到,如果我们对一个区间加上同一个数,并不会改变其中各数的大小关系。所以我们考虑维护每个整块内部始终有序性价比较高。
考虑如下的做法:
-
定义辅助数组
b[],初始时与a[]相同。预处理时,我们在b[]中对所有整块分别排序。这样,a[]与b[]中的块仍然一一对应,而b中每块都有序。复杂度 \(O(n\log n)\) 。 -
考虑区间修改。思路基本同上:对于整块,我们只维护增量,而不重新排序;对于碎块,我们暴力修改,改完后把
a[]中的这一碎块所在整块复制到b[]上,并在b[]上重新排序。复杂度 \(O(\sqrt{n}+\sqrt{n}\log\sqrt{n})\) 。 -
考虑区间查询。对于整块,因为保证块内有序,我们直接在这块内二分查找;对于碎块,直接暴力枚举。复杂度 \(O(\sqrt{n}\log\sqrt{n}+\sqrt{n})\) 。
综上,总复杂度约为 \(O(n\log n+m\sqrt{n}\log\sqrt{n}+m\sqrt{n})\) 。极限数据下运算量在 \(10^7\) 量级,可以通过此题。
代码
cpp
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fastio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define MIKU 0;
using namespace std;
int n, q, a[1000005], b[1000005];
namespace blc {
int siz, cnt;
int op[1005], ed[1005], pos[1000005], add[1005];
void init() {
siz = sqrt(n);
cnt = n % siz ? n / siz + 1 : n / siz;
for(int i=1; i<=cnt; i++) {
op[i] = (i - 1) * siz + 1;
ed[i] = i * siz;
}
ed[cnt] = n;
for(int i=1; i<=n; i++) pos[i] = (i - 1) / siz + 1;
memcpy(b, a, sizeof(a));
for(int i=1; i<=cnt; i++) sort(b+op[i], b+ed[i]+1);
}
void update(int l, int r, int w) {
int L = pos[l], R = pos[r];
if(L == R) {
for(int i=l; i<=r; i++) a[i] += w;
memcpy(b+op[L], a+op[L], siz * 4);
sort(b+op[L], b+ed[L]+1);
} else {
for(int i=L+1; i<=R-1; i++) add[i] += w;
for(int i=l; i<=ed[L]; i++) a[i] += w;
memcpy(b+op[L], a+op[L], siz * 4);
sort(b+op[L], b+ed[L]+1);
for(int i=op[R]; i<=r; i++) a[i] += w;
memcpy(b+op[R], a+op[R], siz * 4);
sort(b+op[R], b+ed[R]+1);
}
}
int query(int l, int r, int c, int res = 0) {
int L = pos[l], R = pos[r];
if(L == R) {
for(int i=l; i<=r; i++) if(a[i] + add[L] >= c) res ++;
} else {
for(int i=L+1; i<=R-1; i++) {
int p = lower_bound(b+op[i], b+ed[i]+1, c-add[i]) - b - op[i] + 1;
res += siz - p + 1;
}
for(int i=l; i<=ed[L]; i++) if(a[i] + add[L] >= c) res ++;
for(int i=op[R]; i<=r; i++) if(a[i] + add[R] >= c) res ++;
}
return res;
}
}
int main() {
fastio;
cin>>n>>q;
for(int i=1; i<=n; i++) cin>>a[i];
blc::init();
while(q--) {
char op; cin>>op;
if(op == 'M') {
int l, r, w; cin>>l>>r>>w;
blc::update(l, r, w);
} else {
int l, r, c; cin>>l>>r>>c;
cout<<blc::query(l, r, c)<<'\n';
}
}
return MIKU;
}