更新:之前写错了,是(fr-fl+1)*u,已改正
例题:codevs 1082(好吧就是改段求段) bzoj 3110(树套树,但可以练手)
1、为什么要有这玩意:当我们用可持久化的数据结构lasy标志可能不能下传,或者用动态开点线段树时如果标记下传会造成无用空间。
2、实现:
以下内容纯属自己YY。
最主要思路,标记不下传,查询时经过一个节点就加上lazy值。
修改函数:大致上跟普通线段树差不多,但是因为标记不下传了,儿子不能得到lazy中的信息,所以维护时要加上自己lazy值
tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c+(r-l+1)*tr[x].u;
查询函数:这个有较大更改,首先我多传了个值u,表示当前lazy值有多少。
对于没有访问过的点,自然直接返回u*区间长度就可以了。
然后在返回值的时候也有把u值加上,访问儿子时加上自己的lazy。
LL findans(int x,int l,int r,int fl,int fr,LL u)
{
if(!x) return u*(LL)(fr-fl+1);
if(l==fl&&fr==r) return tr[x].c+(LL)(r-l+1)*u;
int mid=(l+r)/2;
if(fr<=mid) return findans(tr[x].lc,l,mid,fl,fr,u+tr[x].u);
if(fl>mid) return findans(tr[x].rc,mid+1,r,fl,fr,u+tr[x].u);
return findans(tr[x].lc,l,mid,fl,mid,u+tr[x].u)+findans(tr[x].rc,mid+1,r,mid+1,fr,u+tr[x].u);
}
因为是蒟蒻瞎想的,口胡如有错误,欢迎指出。
code:(codevs 1082)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
struct node{
int lc,rc;
LL c,u;
}tr[400010];int trlen=0;
int n,m,root=0;
void update(int &x,int l,int r,int fl,int fr,LL c)
{
if(!x) x=++trlen;
if(l==fl&&r==fr){tr[x].u+=c;tr[x].c+=c*(r-l+1);return;}
int mid=(l+r)/2;
if(fr<=mid) update(tr[x].lc,l,mid,fl,fr,c);
else if(fl>mid) update(tr[x].rc,mid+1,r,fl,fr,c);
else update(tr[x].lc,l,mid,fl,mid,c),update(tr[x].rc,mid+1,r,mid+1,fr,c);
tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c+(r-l+1)*tr[x].u;
}
LL findans(int x,int l,int r,int fl,int fr,LL u)
{
if(!x) return u*(LL)(fr-fl+1);
if(l==fl&&fr==r) return tr[x].c+(LL)(r-l+1)*u;
int mid=(l+r)/2;
if(fr<=mid) return findans(tr[x].lc,l,mid,fl,fr,u+tr[x].u);
if(fl>mid) return findans(tr[x].rc,mid+1,r,fl,fr,u+tr[x].u);
return findans(tr[x].lc,l,mid,fl,mid,u+tr[x].u)+findans(tr[x].rc,mid+1,r,mid+1,fr,u+tr[x].u);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
update(root,1,n,i,i,(LL)x);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int tmp,l,r,x;scanf("%d %d %d",&tmp,&l,&r);
if(tmp==1) scanf("%d",&x),update(root,1,n,l,r,(LL)x);
else printf("%lld\n",findans(root,1,n,l,r,0LL));
}
}