P2801 教主的魔法
算是分块第一题了吧,抄题解过的。
简化版的题意:给你n个数的序列,支持区间加的修改,同时支持查询区间内大于等于w的数字的多少。
分块题的基本标志就是:xjb查询,普通修改。(来自qsc大佬的一句话)
显然如果用线段树的话维护这个东西就要吐血了。
我们使用分块,把一个序列分成\(\lceil \sqrt{n} \rceil\)块,每一块的长度最多为\(\lfloor \sqrt{n} \rfloor\)。
考虑区间加,我们给一个块加一个lazy,如果是完整的块就直接用lazy加上去,否则就直接暴力修改。复杂度还是\(O(\sqrt{n})\)。
如何查询区间内大于等于w的数字多少?可以用二分。
我们可以对同一个块内的元素排序,成为有序的序列就可以二分了。
注意:代码中有a数组、b数组和分块系列数组。其中a数组表示原顺序的序列,b数组表示分段排序后的序列。
具体看代码,做一道题我也不能总结出多少。
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
const int maxn = 1000005;
int a[maxn], b[maxn], belong[maxn];
int ll[maxn], rr[maxn], lazy[maxn];
int len, num;
int n, m;
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
return s * ans;
}
void init()
{
len = sqrt(n);
for(int i = 1; i <= n; i++) belong[i] = (i - 1) / len + 1;
num = n / len; if(n % len) num++;
for(int i = 1; i <= num; i++)
{
ll[i] = (i - 1) * len + 1; rr[i] = i * len;
}
rr[num] = n;
for(int i = 1; i <= num; i++) std::sort(b + ll[i], b + rr[i] + 1);
}
void reset(int x)
{
for(int i = ll[belong[x]]; i <= rr[belong[x]]; i++) b[i] = a[i];
std::sort(b + ll[belong[x]], b + rr[belong[x]] + 1);
}
void update(int l, int r, int w)
{
if(belong[l] == belong[r])
{
for(int i = l; i <= r; i++) a[i] += w;
reset(l);
return;
}
for(int i = l; i <= rr[belong[l]]; i++) a[i] += w;
for(int i = ll[belong[r]]; i <= r; i++) a[i] += w;
for(int i = belong[l] + 1; i < belong[r]; i++) lazy[i] += w;
reset(l); reset(r);
}
int query(int l, int r, int w)
{
int ans = 0;
if(belong[l] == belong[r])
{
for(int i = l; i <= r; i++) if(a[i] + lazy[belong[l]] >= w) ans++;
return ans;
}
for(int i = l; i <= rr[belong[l]]; i++) if(a[i] + lazy[belong[l]] >= w) ans++;
for(int i = ll[belong[r]]; i <= r; i++) if(a[i] + lazy[belong[r]] >= w) ans++;
for(int i = belong[l] + 1; i < belong[r]; i++) ans += rr[i] - (std::lower_bound(b + ll[i], b + rr[i] + 1, w - lazy[i]) - b) + 1;
return ans;
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; i++) b[i] = a[i] = read();
init();
char opt[3]; int l, r, w;
while(m--)
{
scanf("%s", opt), l = read(), r = read(), w = read();
if(opt[0] == 'M') update(l, r, w);
else if(opt[0] == 'A') printf("%d\n", query(l, r, w));
}
return 0;
}