线段树lazy标记永久化

更新:之前写错了,是(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));
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值