题意
传送门 P2801 教主的魔法
题解
考虑分块,对于整块的元素,排序后,便可以二分答案;对于左右边界不足一块的元素,朴素修改、统计,并对所在块重新进行排序。设块数为 t t t,由于分块规模与 N N N 只有指数上的差异,将 log ( N / t ) \log(N/t) log(N/t) 近似为 O ( log N ) O(\log N) O(logN),那么初始化排序 O ( N log N ) O(N\log N) O(NlogN),单次查询 O ( N / t + t log N ) O(N/t+t\log N) O(N/t+tlogN),单次修改 O ( N / t + N / t log N + t ) O(N/t+N/t\log N+t) O(N/t+N/tlogN+t);当分块大小取 N \sqrt N N,满足 N ≈ Q ( N / t + t ) N\approx Q(N/t+t) N≈Q(N/t+t)。
需要注意的是,排序会修改元素位置,故应保留原始数组;每次排序,将数组复制一份再进行操作。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000005, maxt = 1005;
int N, Q, H[maxn], F[maxn];
int tot, id[maxn], L[maxt], R[maxt], add[maxt];
void rst(int i)
{
memcpy(F + L[i], H + L[i], sizeof(int) * (R[i] - L[i] + 1));
sort(F + L[i], F + R[i] + 1);
}
void change(int l, int r, int x)
{
int p = id[l], q = id[r];
if (p == q)
{
for (int i = l; i <= r; ++i)
H[i] += x;
rst(p);
}
else
{
for (int i = l; i <= R[p]; ++i)
H[i] += x;
for (int i = L[q]; i <= r; ++i)
H[i] += x;
rst(p), rst(q);
for (int i = p + 1; i <= q - 1; ++i)
add[i] += x;
}
}
int ask(int l, int r, int x)
{
int p = id[l], q = id[r], cnt = 0;
if (p == q)
for (int i = l; i <= r; ++i)
cnt += H[i] + add[p] >= x;
else
{
for (int i = l; i <= R[p]; ++i)
cnt += H[i] + add[p] >= x;
for (int i = L[q]; i <= r; ++i)
cnt += H[i] + add[q] >= x;
for (int i = p + 1; i <= q - 1; ++i)
cnt += R[i] - L[i] + 1 - (lower_bound(F + L[i], F + R[i] + 1, x - add[i]) - (F + L[i]));
}
return cnt;
}
int main()
{
scanf("%d%d", &N, &Q);
for (int i = 1; i <= N; ++i)
scanf("%d", H + i);
memcpy(F + 1, H + 1, sizeof(int) * N);
int w = sqrt(N);
for (int i = 1; i <= N; i += w)
{
L[++tot] = i, R[tot] = min(i + w - 1, N);
sort(F + L[tot], F + R[tot] + 1);
}
for (int i = 1; i <= tot; ++i)
for (int j = L[i]; j <= R[i]; ++j)
id[j] = i;
for (int i = 1, l, r, x; i <= Q; ++i)
{
char op;
scanf(" %c%d%d%d", &op, &l, &r, &x);
if (op == 'M')
change(l, r, x);
else
printf("%d\n", ask(l, r, x));
}
}