线段树学习笔记

线段树是对区间进行查询和维护的一种数据结构

对于区间操作比树状数组更加通用

缺点就是又臭又长。。

建树:

void build(int p,int l,int r){
    if(l==r){    
        /*something*/
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    updata(p);
}

 updata(p)中是要维护的数据

如区间和&最值:

void updata(int p){   
    sum[p]=sum[p<<1]+sum[p<<1|1];
    mx[p]=max(mx[p<<1],mx[p<<1|1]);
    mn[p]=min(mn[p<<1],mn[p<<1|1]);
}

将第x个数改成v,也就是单点修改

单点修改:

void change(int p,int l,int r,int x,int val){
	if(l==r){
		/*something*/
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)change(p<<1,l,mid,x,val);
	else change(p<<1|1,mid+1,r,x,val);
	updata(p);
}

查询 l~r 的信息,如区间和

区间查询

int ask(int p,int l,int r,int x,int y){
	if(l>=x&&r<=y)return sum[p];
	int mid=(l+r)>>1,val=0;
	if(x<=mid)val+=ask(p<<1,l,mid,x,y);
	if(y>mid)val+=ask(p<<1|1,mid+1,r,x,y);
	return val; 
}

修改 l~r的信息,如区间和

如果递归逐一更新修改,时间复杂度为 $O(n)$。

要用一个叫延迟标记的东西

延迟标记:

void pushdown(int p,int l,int r){
     if(!tag[p])return;
     int mid=(l+r)>>1;
	tag[p<<1]+=tag[p];
	tag[p<<1|1]+=tag[p];
	sum[p<<1]+=tag[p]*(mid-l+1);
	sum[p<<1|1]+=tag[p]*(r-mid);
	tag[p]=0;
}

区间修改:

void change(int p,int l,int r,int x,int y,int k){
	if(l>=x&&r<=y){
		tag[p]+=k;
		sum[p]+=k*(r-l+1);
		return;		
	}
	pushdown(p,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)change(p<<1|1,l,mid,x,y,k);
	if(y>mid)change(p<<1|1,mid+1,r,x,y,k);
	updata(p);
}

在查询的时候,向下递归时就下传延迟标记

区间查询:

int ask(int p,int l,int r,int x,int y){
	if(l>=x&&r<=y)return sum[p];
	pushdown(p,l,r);
	int mid=(l+r)>>1,val=0;
	if(x<=mid)val+=ask(p<<1,l,mid,x,y);
	if(y>mid)val+=ask(p<<1|1,mid+1,r,x,y);
	return val; 
}

以上就是线段树最基础的操作了

模板题:

https://www.luogu.com.cn/problem/P3372

https://www.luogu.com.cn/problem/P3373

https://www.luogu.com.cn/problem/P2357

蒟蒻刚学一周

以后会继续补充的。。

你可能感兴趣的:(线段树学习笔记)