【洛谷模板题】 线段树、树状数组入门(2)

版权声明: https://blog.csdn.net/leelitian3/article/details/81915327

题目:https://www.luogu.org/problemnew/show/P3372

线段树(含区间修改)

#include<iostream>
using namespace std;
#define LEFT(x) (x << 1)
#define RIGHT(x) ((x << 1) | 1)

long long seg[100005 * 4];
long long delta[100005 * 4];

void pushUp(int a)      //由2个子树更新root
{
    seg[a] = seg[LEFT(a)] + seg[RIGHT(a)];
}

void build(int root, int low, int high)
{
    if(low == high)
    {
        cin >> seg[root];   //输入
        return;
    }
    int mid = (low + high) >> 1;
    build(LEFT(root), low, mid);            //递归建树
    build(RIGHT(root), mid + 1, high);
    pushUp(root);
}

void func(int a, int l, int h, int add)
{
    seg[a] += (h - l + 1) * add;
    delta[a] += add;
}

void pushDown(int a, int l, int h)
{
    if(delta[a] == 0) return;
    int mid = (l + h) >> 1;
    func(LEFT(a), l, mid, delta[a]);

    func(RIGHT(a), mid + 1, h, delta[a]);
    delta[a] = 0;
}

void update(int root, int low, int high, int l, int h, int add)
{
    if(l <= low && high <= h)       //当前区间[low,high]被包含在查询区间[l,r]内部
    {
        seg[root] += add * (high - low + 1);
        delta[root] += add;
        return;
    }
    pushDown(root, low, high);
    int mid = (low + high) >> 1;
    if(l <= mid)                    //区间左边与[l,h]有交集
        update(LEFT(root), low, mid, l, h, add);
    if(h > mid)
        update(RIGHT(root), mid + 1, high, l, h, add);
    pushUp(root);
}

long long getSum(int root, int low, int high, int l, int h)
{
    if(l <= low && high <= h)       //当前区间[low,high]被包含在查询区间[l,r]内部
        return seg[root];
    pushDown(root, low, high);
    long long res = 0;
    int mid = (low + high) >> 1;
    if(l <= mid)                    //区间左边与[l,h]有交集
        res += getSum(LEFT(root), low, mid, l, h);
    if(h > mid)
        res += getSum(RIGHT(root), mid + 1, high, l, h);
    return res;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int n, m, op, a, b, c;
    cin >> n >> m;
    build(1, 1, n);

    while(m--)
    {
        cin >> op;
        if(op == 1)
        {
            cin >> a >> b >> c;
            update(1, 1, n, a, b, c);
        }
        else if(op == 2)
        {
            cin >> a >> b;
            cout << getSum(1, 1, n, a, b) << "\n";
        }
    }
    return 0;
}

树状数组(查分+区间修改)

来介绍一下差分

设数组a[]={1,6,8,5,10},那么差分数组b[]={1,5,2,-3,5}

也就是说b[i]=a[i]-a[i-1];(a[0]=0;),那么a[i]=b[1]+....+b[i];(这个很好证的)。

假如区间[2,4]都加上2的话

a数组变为a[]={1,8,10,7,10},b数组变为b={1,7,2,-3,3};

发现了没有,b数组只有b[2]和b[5]变了,因为区间[2,4]是同时加上2的,所以在区间内b[i]-b[i-1]是不变的.

所以对区间[x,y]进行修改,只用修改b[x]与b[y+1]:

b[x]=b[x]+k;b[y+1]=b[y+1]-k;

 维护查分数组的后缀和

#include <iostream>
using namespace std;
#define lowbit(x) ((x)&(-x))

long long n, m, op, a, b, d;
long long c[500005];               //c[i]保存了Σ([i-lowbit(i)+1,i])

void update(long long x, long long add) //x+lowbit(x)即为其父节点
{
    while(x <= n)
    {
        c[x] += add;
        x += lowbit(x);
    }
}

long long getSum(long long x)       //如求1~6,此时x=6
{
    long long res = 0;        //(110) = (10) + (100)
    while(x)
    {
        res += c[x];
        x -= lowbit(x);
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    a = b = 0;
    cin >> n >> m;
    for(long long i = 1; i <= n; ++i)
    {
        cin >> b;
        update(i, b - a);
        a = b;
    }

    while(m--)
    {
        cin >> op;
        if(op == 1)
        {
            cin >> a >> b >> d;
            update(a, d);
            update(b + 1, -d);
        }
        else if(op == 2)
        {
            cin >> a;
            cout << getSum(a) << "\n";
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/leelitian3/article/details/81915327
今日推荐