线段树1

用到线段树基础知识

#include<bits/stdc++.h>

using namespace std;

// 线段树我默认每个数组下标都是从1开始的

void build(const vector<long long>& nums,vector<long long>& d,int s,int t,int p);
long long getSum(vector<long long>& d,vector<long long>& b,int l,int r,int s,int t,int p);
void update_plus(vector<long long>& d,vector<long long>& b,int l,int r,int s,int t,int p,long long c);

int main()
{
    int n,m,type,l,r;
    long long k;
    cin >> n >> m;
    vector<long long> nums(n+1), d(4*n+1), b(4*n+1, 0);  // 修改这里
    for(int i=1;i<=n;i++) cin>>nums[i];
    build(nums,d,1,n,1);
    while(m--)
    {
        cin >> type;
        if(type==2)
        {
            cin >> l >> r;
            cout << getSum(d,b,l,r,1,n,1) << '\n';
        }
        else if(type==1)
        {
            cin >> l >> r >> k;
            update_plus(d,b,l,r,1,n,1,k);
        }
    }
    return 0;
}


void build(const vector<long long>& nums,vector<long long>& d,int s,int t,int p)
{
    if(s==t)
    {
        d[p] = nums[s];
        return;
    }
    int m = s + ((t-s)>>1);
    build(nums,d,s,m,2*p);
    build(nums,d,m+1,t,2*p+1);
    d[p] = d[2*p] + d[2*p+1];
}

long long getSum(vector<long long>& d,vector<long long>& b,int l,int r,int s,int t,int p)
{
    if(l<=s && t<=r)
    {
        return d[p];
    }
    int m = s + ((t-s)>>1);
    long long sum=0;
    if (b[p] && s != t) //下传懒标记
    {  
        d[p*2] += b[p]*(m-s+1);
        d[p*2+1] += b[p]*(t-m);
        b[p*2] += b[p];
        b[p*2+1] += b[p];
        b[p] = 0;
    }
    if(l<=m) sum += getSum(d,b,l,r,s,m,2*p);
    if(r>m) sum += getSum(d,b,l,r,m+1,t,2*p+1);  //等价于r>=m+1
    return sum;
}

void update_plus(vector<long long>& d,vector<long long>& b,int l,int r,int s,int t,int p,long long c)
{
    // 当前区间为修改区间的子集时直接修改当前节点的值,修改后标记
    if (l <= s && t <= r) {
        d[p] += (t - s + 1) * c, b[p] += c;
        return;
    }
    int m = s + ((t - s) >> 1);
    if (b[p] && s != t) //下放标记(不会有s==t的情况因为s==t的时候肯定是lr的子区间已经直接return了)
    {
        d[p * 2] += b[p] * (m - s + 1), d[p * 2 + 1] += b[p] * (t - m);
        b[p * 2] += b[p], b[p * 2 + 1] += b[p];
        b[p] = 0;
    }
    if (l <= m) update_plus(d, b, l, r, s, m, p * 2, c);
    if (r > m) update_plus(d, b, l, r, m + 1, t, p * 2 + 1, c);
    d[p] = d[p * 2] + d[p * 2 + 1];
}

这里用了懒惰区间标记。对有些当次不需要访问但是确实有变化的区间进行了标记,如果下次访问到了标记区间,则执行修改并下放标记。

nums,d,b分别代表原数组,区间数组((从二叉树顶部为1开始(结点p子树为2p,2p+1)),结点懒惰标记数组。

update与getSum都需要用到标记判断与下放,判断条件是b[p] && s != t。