zoukankan      html  css  js  c++  java
  • 浅谈线段树

    线段树的性质

    1.定义时要开4倍空间

    struct tree{
    	long long lazy,s;
    	long long maxx,minn;
    }t[N<<2];
    

    注意别 <<4 容易 MLE 是<<2

    为什么开4倍空间
    4倍空间原因

    2.左右儿子

    ll ls(ll p){ return p<<1;}
    ll rs(ll p){ return p<<1|1;}
    

    3.建树

    void build(ll p,ll l, ll r){
    	//t[p],l=l,t[p].r=r;一般没用
    	  t[p].add=0,t[p].mu=1;
            //add 加的懒惰标记
            //mu  乘的懒惰标记
    	if(l==r){ t[p].sum=num[l];return ; }
    	ll mid=l+(r-1)>>1;
            //防止爆long long 或者 int
    	  build(ls(p),l,mid),build(rs(p),mid+1,r);
    	  push_up_sum(p); 
        //访问到根节点时回溯,向上建树
    }
    

    4. 维护区间的和(最大值/最小值)

    void push_up_sum(ll p){
    	return t[p].s= t[ls(p)].s + t[rs(p)].s;
    }
    void push_up_max(ll p){
    	t[p].maxx=max(t[ls(p)].maxx,t[rs(p)].maxx);
    }
    void push_up_min(ll p){
    	t[p].minn=min(t[ls(p)].minn,t[rs(p)].minn);
    }
    

    5.区间加与区间乘同时进行(PS:其实加也可以是减,因为输入的K如果是负数,会按照负数进行加)

    void mu(ll p,ll L,ll R,ll l,ll r,ll k){
        if(l<=L&&R<=r){ 
        t[p].sum=t[p].sum*k%mod;
        t[p].mu=t[p].mu*k%mod;
        t[p].add=t[p].add*k%mod;
        return ;}
        push_down(p,L,R);
        ll mid=L+(R-1)>>1;
        if(l<=mid)  mu(ls(p),L,mid,l,r,k);
        if(mid<r)   mu(rs(p),mid+1,R,l,r,k);
        push_up_sum(p);
        //最后一定要更新树的值(回溯时完成)
    }
    void add(ll p,ll L,ll R,ll l,ll r,ll k){
        if(l<=L&&R<=r){
            t[p].add = (t[p].add+k);
            t[p].sum = t[p].sum + k*(R-L+1);    
            return;
        }
        push_down(p,L,R);
        ll mid = L+(R-1)>>1;
        if(l<=mid) add(ls(p),L,mid,l,r,k);
        if(mid<r)  add(rs(p),mid+1,R,l,r,k);
        push_up_sum(p);
        //同理,最后一定要更新树的值(回溯时完成)
    }
       
    

    其实这里没什么区别都是只要在P的区间L-->R位于要更新的l->r的区间内,那么就懒惰处理,区间和的更改(进行数学计算)

    6.重点!push_down函数,懒惰标记的下传

    void push_down(ll p,ll L,ll R){
    	ll mid=L+(R-1)>>1;
    	t[ls(p)].sum=(t[ls(p)].sum*t[p].mu+t[p].add*(mid-L+1))%mod;  
    	t[rs(p)].sum=(t[rs(p)].sum*t[p].mu+t[p].add*(R-mid))%mod;
    	t[ls(p)].mu=(t[ls(p)].mu*t[p].mu)%mod;
        t[rs(p)].mu=(t[rs(p)].mu*t[p].mu)%mod;
    	t[ls(p)].add=(t[ls(p)].add*t[p].mu+t[p].add)%mod;
    	t[rs(p)].add=(t[rs(p)].add*t[p].mu+t[p].add)%mod;
    	t[p].add=0;		t[p].mu=1;
    	return ;
    }
    

    7.区间和,这个背过就好,所有的线段树都一样

    ll query(ll p,ll L,ll R,ll l,ll r){
        ll ans=0;
        if(l<=L&&R<=r) return t[p].sum;
        ll mid=L+(R-1)>>1;
        push_down(p,L,R);
        if(l<=mid) ans+=query(ls(p),L,mid,l,r);
        if(r>mid)  ans+=query(rs(p),mid+1,R,l,r);
        return ans;
    }
    

    8.根据二叉树的性质可以得到一个检验的树的程序

    void print(ll p){
    	if(t[p].s==0) return ;
    	printf("%d ",t[p].s);
    	print(ls(p)),print(rs(p));
    	printf("
    ");
    }
    

    9.区间替换暂时鸽掉,哪天脑子清晰再来写

    后记:感觉线段树2的代码可以水掉线段树1;

    code

    #include<iostream>
    #include<cstdio>
    #define ll long long
    
    using namespace std;
    const int N=1e5+10;
    ll n,m,mod;
    struct tree{
        ll add,mu,sum;
    }t[N<<2];
    ll num[N];
    ll ls(ll p){ return p<<1;}
    ll rs(ll p){ return p<<1|1;}
    ll push_up_sum(ll p){
        return t[p].sum= t[ls(p)].sum + t[rs(p)].sum;
    }
    void push_down(ll p,ll L,ll R){
    	ll mid=L+(R-1)>>1;
    	t[ls(p)].sum=(t[ls(p)].sum*t[p].mu+t[p].add*(mid-L+1))%mod;  
    	t[rs(p)].sum=(t[rs(p)].sum*t[p].mu+t[p].add*(R-mid))%mod;
    	t[ls(p)].mu=(t[ls(p)].mu*t[p].mu)%mod;
        t[rs(p)].mu=(t[rs(p)].mu*t[p].mu)%mod;
    	t[ls(p)].add=(t[ls(p)].add*t[p].mu+t[p].add)%mod;
    	t[rs(p)].add=(t[rs(p)].add*t[p].mu+t[p].add)%mod;
    	t[p].add=0;		t[p].mu=1;
    	return ;
    }
    void mu(ll p,ll L,ll R,ll l,ll r,ll k){
        if(l<=L&&R<=r){ 
        t[p].sum=t[p].sum*k%mod;
        t[p].mu=t[p].mu*k%mod;
        t[p].add=t[p].add*k%mod;
        return ;}
        push_down(p,L,R);
        ll mid=L+(R-1)>>1;
        if(l<=mid)  mu(ls(p),L,mid,l,r,k);
        if(mid<r)   mu(rs(p),mid+1,R,l,r,k);
        push_up_sum(p);
    }
    void add(ll p,ll L,ll R,ll l,ll r,ll k){
        if(l<=L&&R<=r){
            t[p].add = (t[p].add+k);
            t[p].sum = t[p].sum + k*(R-L+1);    
            return;
        }
        push_down(p,L,R);
        ll mid = L+(R-1)>>1;
        if(l<=mid) add(ls(p),L,mid,l,r,k);
        if(mid<r)  add(rs(p),mid+1,R,l,r,k);
        push_up_sum(p);
    }
    void build(ll p,ll l, ll r){
        t[p].add=0,t[p].mu=1;
        if(l==r){ t[p].sum=num[l];return ; }
        ll mid=l+(r-1)>>1;
        build(ls(p),l,mid),build(rs(p),mid+1,r);
        push_up_sum(p); 
    }
    ll query(ll p,ll L,ll R,ll l,ll r){
        ll ans=0;
        if(l<=L&&R<=r) return t[p].sum;
        ll mid=L+(R-1)>>1;
        push_down(p,L,R);
        if(l<=mid) ans+=query(ls(p),L,mid,l,r);
        if(r>mid)  ans+=query(rs(p),mid+1,R,l,r);
        return ans;
    }
    int main(){
    	scanf("%lld%lld%lld",&n,&m,&mod);
    	for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
    	build(1,1,n);
    //	print();
    	int tpy;ll x,y,k;
    	while(m--){
    		scanf("%d",&tpy);
    		switch(tpy){
    			case 1:{
    				scanf("%lld%lld%lld",&x,&y,&k);
    				mu(1,1,n,x,y,k);
    			//	puts("");
    			//	print();
    				break;
    			}
    			case 2:{
    				scanf("%lld%lld%lld",&x,&y,&k);
    				add(1,1,n,x,y,k);
    			//	puts("");
    			//	print();
    				break;
    			}
    			case 3:{
    				scanf("%lld%lld",&x,&y);
    				printf("%lld
    ",query(1,1,n,x,y)%mod);
    				break;
    			}
    		}
    	}
    }
    
  • 相关阅读:
    java:编写jar包加密工具,防止反编译
    Linux:CentOS-7常用命令
    MyBatis:MyBatis-Plus条件构造器EntityWrapper
    SpringBoot:SpringBoot项目的配置文件放在Jar包外加载
    机械学习导
    如何選擇最佳的 Wi-Fi 無線網路頻道,獲得最佳的傳輸速度(转载)
    解决ubuntu下filezilla登录ftp看不到中文目录和文件的问题 (转载)
    输入前几个字母,用箭头就能找出开头字母相同的历史命史
    Linux系统下使用split命令分割大文件 (转载)
    删除文件时排除特定文件 (转载)
  • 原文地址:https://www.cnblogs.com/-wzd233/p/13911767.html
Copyright © 2011-2022 走看看