浅谈线段树

线段树的性质

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("\n");
}

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\n",query(1,1,n,x,y)%mod);
				break;
			}
		}
	}
}
posted @ 2020-11-01 21:30  Imy_bisLy  阅读(155)  评论(0编辑  收藏  举报