P3372 【模板】线段树 1

发布时间 2024-01-02 20:31:32作者: 纯粹的

原题链接

题后感

码量也太大了吧

小记

题解网上有,但是有关这个lazytag我要提一嘴,我建议不要记它,你只需知道修改的区间没有整体破坏时,其内部的元素内容暂不做修改

code 码量真大

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll tree[400020]={0};
ll save[400020]={0};
ll a[100020]={0};
void keep(ll now,ll len,ll k)
{//代表传给后代值,现在区间,区间长度,传递值
    save[now]+=k;//给子节点的值,先在你这保留着,反正也没拆开
    tree[now]+=len*k;//区间值
}

void breakdown(ll now,ll l,ll r)//这个区间要被拆开来,所以保留的值传给后代
{//代表当前区间,区间左端点,区间右端点
    ll mid=(l+r)/2;
    keep(2*now,mid-l+1,save[now]);
    keep(2*now+1,r-mid,save[now]);
    save[now]=0;//传递过去了,自然要清空
}

void add(ll now,ll l,ll r,ll L,ll R,ll k)//
{
    if(l>=L&&r<=R)//如果当前区间被完全包裹
        keep(now,r-l+1,k);//不用分开来传递了
    else
    {
        if(!(r<L||l>R))//如果有交集
        {
            breakdown(now,l,r);//要被拆开了,把保留着的值传给子节点
            int mid=(l+r)/2;
            add(2*now,l,mid,L,R,k);
            add(2*now+1,mid+1,r,L,R,k);
            tree[now]=tree[2*now]+tree[2*now+1];
        }
    }
}

void build(ll now,ll l,ll r)
{
    if(l==r)
    {
        tree[now]=a[l];
        return;
    }
    ll mid=(l+r)/2;
    build(2*now,l,mid);
    build(2*now+1,mid+1,r);
    tree[now]=tree[2*now]+tree[2*now+1];
}

ll query(ll now,ll l,ll r,ll L,ll R)//用于找出完全组成待找区间的区间
{
    if(l>=L&&r<=R) return tree[now];
    else if(!(r<L||l>R))
    {
        ll mid=(l+r)/2;
        breakdown(now,l,r);
        return query(2*now,l,mid,L,R)+query(2*now+1,mid+1,r,L,R);
    }
    else return 0;
}
int main()
{
    ll n,m;
    cin>>n>>m;
    for(ll i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);//构建线段树,
    while(m--)
    {
        ll op,x,y;
        cin>>op>>x>>y;
        if(op==1)
        {
            ll k;
            cin>>k;
            add(1,1,n,x,y,k);//待加区间肯定被包裹在根区间里,
        }
        else cout<<query(1,1,n,x,y)<<endl;
    }
    return 0;
}