学习记录 线段树懒标记

对线段树进行区间更新时,采用一般方法需要访问区间每一个元素对应的所有叶节点 并逐级向上更新,这一操作的复杂度可以达到 O ( n l o g n ) O(nlogn) O(nlogn),比朴素方法还要慢。因此引入懒标记,更新时更新到能包含整个区间的结点时就给那个点打上标记,需要查询时再下传到左右儿子,这样可以显著提升效率。
懒标记讲解
对某个节点的懒标记进行下放时,左右儿子的标记也要更新,并且该节点的标记值要清零,以防重复下放。

用懒标记解决洛谷P3372

#include <cstdio>
#include <algorithm>
#define long long long
const int M=(int)1e5+1;
using namespace std;
long dat[M*4],tag[M*4]; //tag为懒标记,存储曾经加上过的值备用
int n,num;
inline void lazy_tag(int k,int l,int r) {
    if(tag[k]) {
    	//下放到左右儿子 
        int left=k*2+1,right=k*2+2;
        int child_len=(r-l+1)/2;  //求子区间的长度
        dat[left]+=tag[k]*child_len;
        dat[right]+=tag[k]*child_len;
        tag[left]+=tag[k];
        tag[right]+=tag[k];  //记得为左右打上标记
        //清零,防止重复下放
        tag[k]=0;
    }
}

void update(int st,int end,long val,int k=0,int l=0,int r=n) {
    //当前点对应的区间不合法
    if(l>end||r<st)    return;
    if(l>=st&&r<=end) {
        dat[k]+=val*(long)(r-l+1);
        tag[k]+=val;  //注意打标记
        return;
    }
    //防止之前有修改过的值,因此update也要将懒标记下放
    //不需要一次性下放到叶节点,遍历到需要的点时会再次下放
    lazy_tag(k,l,r);
    //计算左右加上的大小
    int lc=k*2+1,rc=k*2+2;
    update(st,end,val,lc,l,(l+r)/2);
    update(st,end,val,rc,1+(l+r)/2,r);
    dat[k]=dat[lc]+dat[rc]; //更新
}

long query(int st,int end,int k=0,int l=0,int r=n) {
	if(l>end||r<st)    return 0;
	if(l>=st&&r<=end)	return dat[k]; 
	lazy_tag(k,l,r);
	int lc=k*2+1,rc=k*2+2;
	long L=query(st,end,lc,l,(l+r)/2);
	long R=query(st,end,rc,1+(l+r)/2,r);
	return L+R;
}

int main(){
    int m;
    scanf("%d%d",&n,&m);
    int i,t=n;
    for(i=0; (1<<i)<n; i++);
    num=(1<<(i+1))-1;
    n=(1<<i)-1; //更新n的值为2的整数次幂方便使用
    for(int i=0; i<t; i++) {
        long x;
        scanf("%lld",&x);
        update(i,i,x);
    }
    fill(tag,tag+num,0);
    while(m--) {
    	short op;
    	scanf("%hd",&op);
    	if(op==1) {
    		int x,y;
    		long k;
    		scanf("%d%d%lld",&x,&y,&k);
    		update(x-1,y-1,k);
		}
		else {
			int x,y;
			scanf("%d%d",&x,&y);
			printf("%lld\n",query(x-1,y-1));
		}
	}
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用el-tree的加载功能来实现嵌套的el-tree。加载是一种加载数据的方式,只有在需要展开节点时才会加载子节点的数据,可以有效地减少初始加载的数据量。 首先,您需要在父节点的数据中添加一个`hasChildren`字段,用于标识该节点是否有子节点。然后,在父节点的`load`方法中请求子节点的数据,并将数据通过`resolve`方法返回。接着,您可以通过设置`lazy`属性为`true`来启用加载功能。 下面是一个示例代码: ```html <template> <el-tree :data="treeData" :load="lazyLoad" :lazy="true"></el-tree> </template> <script> export default { data() { return { treeData: [ { label: '父节点1', hasChildren: true }, { label: '父节点2', hasChildren: true } ] }; }, methods: { lazyLoad(node, resolve) { // 模拟异步请求子节点数据 setTimeout(() => { const children = [ { label: '子节点1' }, { label: '子节点2' } ]; // 通过resolve方法返回子节点数据 resolve(children); }, 1000); } } }; </script> ``` 在上述示例中,父节点1和父节点2都被标识为有子节点,当这些父节点被展开时,会触发`lazyLoad`方法进行数据加载。在`lazyLoad`方法中,我们可以通过异步请求获取子节点数据,并通过`resolve`方法返回给el-tree组件。 您可以根据实际情况修改请求子节点数据的方式,如使用AJAX请求或者从后端接口获取数据。同时,您也可以根据需求自定义节点的展示样式或添加其他功能。 希望以上信息对您有所帮助!如有更多问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值