luo3372线段树模板的分块做法

题目大意

请你维护一个有n个元素的整数序列,要求支持区间查询&区间修改

对于100%的数据,\(1<=n<=10^5\)

分析

正常做法是线段树维护区间修改、区间查询,今天我要讲的是一种暴力做法:分块

分块的思想并不复杂,分块把一个长度为n的区间分成num段,操作时如果是整段用标记修改,不是整段的部分暴力修改

分析时间复杂度:在这题中,每段的标记修改是\(O(1)\)的,最多有num段,整段标记修改所用时间是\(O(num)\)的;不是整段的部分最多有\(O(n/num)\)个,暴力修改所用的时间是\(O(n/num)\)的;所以总时间是\(O(num+n/num)\)

根据基本不等式,num取\(\sqrt n\)时该式有最小值;所以num取\(\sqrt n\)

实现

分块的思想并不复杂,时间复杂度也不玄学,但是实现起来并不方便(可能是我弱)

分块的修改/查询都分为2部分:

  1. 整块的修改
  2. “零头”的修改

整块的修改是否简便:add[i] += val;
“零头”的修改直接修改,同时不要忘了维护所在块的信息:

a[i] += val;
sum[id[i]] += val;

修改就愉快地解决了,查询和修改差不多。

完整代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn = 100007;
int n, m, num, id[maxn];
long long sum[1000], add[1000], a[maxn];

int main(){
    scanf("%d%d", &n, &m);
    num = sqrt(n);
    for (int i = 1; i <= n; ++i){
        scanf("%lld", &a[i]);
        id[i] = (i-1) / num;
        sum[id[i]] += a[i];
    }

    while (m--){
        int d; scanf("%d", &d);
        if (d==1){
            int x, y, C; scanf("%d%d%d", &x, &y, &C);
            int Leftid = (x-1) / num + 1;
            int Rightid = (y-1) / num - 1;

            long long res = 0;
            for (int i = Leftid; i <= Rightid; ++i)
                 add[i] += C;

            for (int i = x; i <= Leftid * num; ++i)
                a[i] += C, sum[id[i]] += C;

            for (int i = (Rightid+1)*num+1; i <= y; ++i)
                a[i] += C, sum[id[i]] += C;
        }else{
            int x, y; scanf("%d%d", &x, &y);
            int Leftid = (x-1) / num + 1;
            int Rightid = (y-1) / num - 1;

            if (id[x] == id[y]){
                long long res = 0;
                for (int i = x; i <= y; ++i)
                    res += a[i] + add[id[i]];
                printf("%lld\n", res);
                continue;
            }

            long long res = 0;
            for (int i = Leftid; i <= Rightid; ++i)
                res += sum[i] + add[i] * num;

            for (int i = x; i <= Leftid * num; ++i)
                res += a[i] + add[Leftid-1];

            for (int i = (Rightid+1)*num+1; i <= y; ++i)
                res += a[i] + add[Rightid+1];

            printf("%lld\n", res);

        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/YJZoier/p/9721774.html