作为一个蒟蒻不会打线段树和树状数组,这样的话分块就是一个很好的选择。
如有侵权,请联系我(周末联系)
下面先说说什么是分块
分块算法实质上是一种是通过分成多块后在每块上打标记以实现快速区间修改,区间查询的一种算法。其均摊时间复杂度为O(√n)
分块算法相较于各种树形数据结构,具有简便易写,方便调试等多种优点。在同等数据规模下,如1e5,其时间效率并不会低太多,在考试时反而是一种有力的得分方法。
接下来讲一下分块算法的基本操作及性质:
为了使得其有着最稳定的时间复杂度,我们经常讲一个长度为n的序列分为(√n)
个大小为(√n)的块,如果n不是完全平方数,则序列最右端会多出一个角块
先看这张图

该长度为10的序列被分为了4块,前三块的大小为√10的近似值3,最后一个角块大小为11
而我们要记录的一个值,就是每个序号代表的数,属于哪一块
如上图 1,2,3就属于第一块 4,5,6就属于第二块 7,8,9就属于第三块,10就属于第四块
可以得到获取每一个序号的所在块的代码是:
int n;//总个数
int block=sqrt(n);//每一块大小
for(int i=1;i<=n;i++)
{
belong[i]=(i-1)/block+1;//每一个数所在块
}
最大值怎么求?
再看这张图

还是拿这张图,我们现在给每个点上加了一个权值,每个块维护一下块内最大值。
当我们查询任意一个区间[l,r][l,r]时,如果ll所在的块与rr所在的块相同,如[1,2][1,2],则直接暴力查询即可若其不在一个块但是块是相邻的,一样是暴力查询若其块不相邻,如[1,10][1,10],我们先处理两边的边块角块,先暴力查询11和1010所在的块内最大值,最后直接查询中间块内最大值即可。
那如果加入了区间修改,又该怎么办呢?
对于整块修改,我们打个加法标记,即当前块增加了多少,最大值相应的就增加了多少
而多于边块角块,暴力修改,特判最大值即可
所以总时间复杂度也是O(√n)
分块还能解决很多很麻烦的问题,比如寻找区间内前驱后继
分块解法也非常的好理解,当我们分块的时候,就对每一个块进行排序,查找时,边块角块依旧暴力,整块使用lower_bound和upper_bound二分查找即可
文字转载:https://www.luogu.org/blog/48265/qian-tan-ji-chu-gen-hao-suan-fa-fen-kuai
样例代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100007;
long long n, m, num, id[maxn];
long long sum[maxn], add[maxn], a[maxn];
bool lucky_check(long long x)
{
int t;
while(x!=0)
{
t=x%10;
if(t!=4&&t!=7)
return false;
x/=10;
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
num = sqrt(n);
for (long long i = 1; i <= n; ++i)
{
scanf("%lld", &a[i]);
id[i] = (i-1) / num;
sum[id[i]] += a[i];
}
while (m--)
{
string d;
cin>>d;
if (d=="add")
{
long long l, r, d;
scanf("%lld%lld%lld", &l, &r, &d);
long long Leftid = (l-1) / num + 1;
long long Rightid = (r-1) / num - 1;
long long res = 0;
for (long long i = Leftid; i <= Rightid; ++i)
add[i] += d;
for (long long i = l; i <= Leftid * num; ++i)
a[i] += d, sum[id[i]] += d;
for (long long i = (Rightid+1)*num+1; i <= r; ++i)
a[i] += d, sum[id[i]] += d;
}
else
{
long long l, r;
scanf("%lld%lld", &l, &r);
long long Leftid = (l-1) / num + 1;
long long Rightid = (r-1) / num - 1;
if (id[l] == id[r])
{
long long res = 0;
for (long long i = l; i <= r; ++i)
res+=a[i] + add[id[i]];
printf("%lld
", res);
continue;
}
long long res = 0;
for (long long i = Leftid; i <= Rightid; ++i)
res+=sum[i] + add[i] * num;
for (long long i = l; i <= Leftid * num; ++i)
res+=a[i] + add[Leftid-1];
for (long long i = (Rightid+1)*num+1; i <= r; ++i)
res+=a[i] + add[Rightid+1]
printf("%lld
", res);
}
}
return 0;
}