2022.4.3 线段树模板
其实还是挺简单的,刚开始迷惑的只有一点,就是懒惰值的作用。
其实就是让该节点的子节点暂时不更新值以节约时间的。例如,1—5的区间都加上2,可以只在这个区间对应的区间和上加上(5-1+1)*2.并把2加入到这个节点对应的懒惰值中。当后续想要使用
它的子节点的区间时,比如1—3,就可以将这里的懒惰值归零并让1—3与4—5对应的懒惰值加2,随后这两个区间的和也加上这个值*元素的个数就行了。
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAXN 1000001
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline ll ls(ll x)//用于求父亲节点x的左儿子
{
return x<<1;//位运算,比用运算符好像是快一点。这里相当于x*2
}
inline ll rs(ll x)//用于求父亲节的x的右儿子
{
return x<<1|1;//同上,相当于x*2+1
}
inline void ans_up(ll p)//更新p节点的ans,即该节点对应区间的和
{
ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r)//建立线段树。
{
tag[p]=0;//懒惰标志,可以优化时间
if(l==r)//该情况下说明当前区间只有一个元素,为叶子,真正地赋值。
{
ans[p]=a[l];
return;
}
ll mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
ans_up(p);//在递归回来的过程中求出每个节点对应区间的和
}
inline void f(ll p,ll l,ll r,ll k)
{
tag[p]+=k;
ans[p]=ans[p]+k*(r-l+1);//使父节点的懒惰值下分至子节点并更新。
}
inline void push_data(ll p,ll l,ll r)//如果用到一个懒惰标志已赋值的区间的子区间,就需要把懒惰值下发。
{
ll mid=(l+r)>>1;
if(tag[p])//我自己加的,大概能优化一点点时间叭~
{
f(ls(p),l,mid,tag[p]);
f(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
}
void change_data(ll nl,ll nr,ll l,ll r,ll p,ll k)//用于更改区间值
{
if(nl<=l&&nr>=r)
{
ans[p]+=k*(r-l+1);
tag[p]+=k;
return ;
}
push_data(p,l,r);
ll mid=(l+r)>>1;
if(nl<=mid)
change_data(nl,nr,l,mid,ls(p),k);
if(nr>mid)
change_data(nl,nr,mid+1,r,rs(p),k);
ans_up(p);//更新区间和
}
ll ssum(ll nl,ll nr,ll l,ll r,ll p)//用于输出区间和
{
ll sum=0;
if(nl<=l&&nr>=r)
return ans[p];
ll mid=(l+r)>>1;
push_data(p,l,r);//下放懒惰值
if(nl<=mid)
sum+=ssum(nl,nr,l,mid,ls(p));
if(nr>mid)
sum+=ssum(nl,nr,mid+1,r,rs(p));
return sum;
}
int main()
{
ll x,y,k,flag;
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
build(1,1,n);
while(m--)
{
scanf("%lld",&flag);
if(flag==1)
{
scanf("%lld%lld%lld",&x,&y,&k);
change_data(x,y,1,n,1,k);
}
else
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",ssum(x,y,1,n,1));
}
}
return 0;
}

浙公网安备 33010602011771号