1. 程式人生 > 實用技巧 >線段樹小記

線段樹小記

普通線段樹可以[先標記\(tag\)後更改]或[邊推\(tag\)邊更改]

打算從原來的[先標記\(tag\)後更改]改變為較為普及的[邊推\(tag\)邊更改]

這裡放模板題程式碼

#include<cstdio>
#include<iostream>
#define N 100005
#define ll long long
using namespace std;
struct node
{
    int l,r;
    ll s,lz;
}t[N << 2];
ll a[N];
int n,m,opt,x,y;
ll z;
void merge(int p)
{
    t[p].s=t[p+p].s+t[p+p+1].s;
}
void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r;
    if (l==r)
    {
        t[p].s=a[l];
        t[p].lz=0;
        return;
    }
    int mid=(l+r) >> 1;
    build(p+p,l,mid);
    build(p+p+1,mid+1,r);
    merge(p);
}
void down_tag(int p,ll z)
{
    t[p].s+=z*(t[p].r-t[p].l+1);
    t[p].lz+=z;
}
void down(int p)
{
    down_tag(p+p,t[p].lz),down_tag(p+p+1,t[p].lz);
    t[p].lz=0;
}
void change(int p,int x,int y,ll z)
{
    if (t[p].l>y||t[p].r<x)
        return;
    if (t[p].l>=x&&t[p].r<=y)
    {
        t[p].s+=z*(t[p].r-t[p].l+1);
        t[p].lz+=z;
        return;
    }
    down(p);
    change(p+p,x,y,z);
    change(p+p+1,x,y,z);
    merge(p);
}
ll calc(int p,int x,int y)
{
    if (t[p].l>y||t[p].r<x)
        return 0;
    if (t[p].l>=x&&t[p].r<=y)
        return t[p].s;
    down(p);
    return calc(p+p,x,y)+calc(p+p+1,x,y);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    build(1,1,n);
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&opt);
        switch (opt)
        {
            case 1:
                scanf("%d%d%lld",&x,&y,&z);
                change(1,x,y,z);
                break;
            case 2:
                scanf("%d%d",&x,&y);
                printf("%lld\n",calc(1,x,y));
                break;
        }
    }
}