[模板]线段树

前言:

线段树的介绍略,可参见《算法进阶》P210~P223
内容详见视频课:
线段树及其应用

模板代码:

1.单点修改,区间查询区间和

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
char opt;//类型符,C修改,Q查询 
int x,v,l,r;//C指把a[x]改为v,Q指询问区间[l,r]上的区间和 
int n,m,a[MAXN];
inline int read(){//快读int
	int x=0,f=1;char ch=getchar();
	if(!isdigit(ch)){if(ch==45)f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
char get(){//快读char 
	char ch=getchar();
	while(isdigit(ch))ch=getchar();
	return ch; 
}
struct SegmentTree{
	int l,r;//左右端点 
	int sum;//附加信息,如区间和
}tree[MAXN*4];
void build(int p,int l,int r){
	tree[p].l=l;tree[p].r=r;//确定树的左右端点 
	if(l==r){tree[p].sum=a[l];return ;}//如果左右相等,那么确定附加信息并返回
	int mid=(l+r)/2;
	build(p*2,l,mid);//递归构造左子树 
	build(p*2+1,mid+1,r);//递归构造右子树 
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//向上更新信息,pushup  
}
void print(int p){//打印线段树,可用于调试 
	printf("%d %d %d %d\n",p,tree[p].l,tree[p].r,tree[p].sum);//输出对应信息
	if(tree[p].l==tree[p].r)return ;
	print(p*2);
	print(p*2+1);
}
void update(int p,int x,int v){//单点修改,将a[x]修改为v 
	if(tree[p].l==tree[p].r) {tree[p].sum=v;return;}//找到目标,直接更新 
	//折半查找(修改点x不在左半就在右半) 
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点 
	if(x<=mid)update(p*2,x,v);//递归更新左半
	else update(p*2+1,x,v);//递归更新右半
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//pushup
}
int query(int p,int l,int r){//返回区间[l,r]的区间和 
	//若目标区间完全包含节点p代表的区间,直接返回区间值
	if(l<=tree[p].l&&tree[p].r<=r)return tree[p].sum;
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点
	/*int val=0;
	if(l<=mid)val+=query(p*2,l,r);
	if(r>mid)val+=query(p*2+1,l,r);
	return val;*///版本1
	if(r<=mid)return query(p*2,l,r);//完全在左半 
	else if(l>mid)return query(p*2+1,l,r);//完全在右半 
	else return query(p*2,l,r)+query(p*2+1,l,r);//横跨中点
	//版本2 
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	//print(1);
	for(int i=1;i<=m;i++){
		opt=get();
		if(opt=='C'){
			x=read(),v=read();
			update(1,x,v);
		}
		else{
			l=read(),r=read();
			printf("%d\n",query(1,l,r));
		}
	}
	return 0;
}

2.单点修改,区间查询最大值

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
char opt;//类型符,C修改,Q查询 
int x,v,l,r;//C指把a[x]改为v,Q指询问区间[l,r]上的区间最大值 
int n,m,a[MAXN];
inline int read(){//快读int
	int x=0,f=1;char ch=getchar();
	if(!isdigit(ch)){if(ch==45)f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
char get(){//快读char 
	char ch=getchar();
	while(isdigit(ch))ch=getchar();
	return ch; 
}
struct SegmentTree{
	int l,r;//左右端点 
	int maxn;//附加信息,如区间最大值 
}tree[MAXN*4];
void build(int p,int l,int r){
	tree[p].l=l;tree[p].r=r;//确定树的左右端点 
	if(l==r){tree[p].maxn=a[l];return ;}//如果左右相等,那么确定附加信息并返回
	int mid=(l+r)/2;
	build(p*2,l,mid);//递归构造左子树 
	build(p*2+1,mid+1,r);//递归构造右子树 
	tree[p].maxn=max(tree[p*2].maxn,tree[p*2+1].maxn);//向上更新信息,pushup  
}
void print(int p){//打印线段树,可用于调试 
	printf("%d %d %d %d\n",p,tree[p].l,tree[p].r,tree[p].maxn);//输出对应信息
	if(tree[p].l==tree[p].r)return ;
	print(p*2);
	print(p*2+1);
}
void update(int p,int x,int v){//单点修改,将a[x]修改为v 
	if(tree[p].l==tree[p].r) {tree[p].maxn=v;return;}//找到目标,直接更新 
	//折半查找(修改点x不在左半就在右半) 
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点 
	if(x<=mid)update(p*2,x,v);//递归更新左半
	else update(p*2+1,x,v);//递归更新右半
	tree[p].maxn=max(tree[p*2].maxn,tree[p*2+1].maxn);//pushup  
}
int query(int p,int l,int r){//返回区间[l,r]的区间最大值 
	//若目标区间完全包含节点p代表的区间,直接返回区间值
	if(l<=tree[p].l&&tree[p].r<=r)return tree[p].maxn;
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点
	if(r<=mid)return query(p*2,l,r);//完全在左半 
	else if(l>mid)return query(p*2+1,l,r);//完全在右半 
	else return max(query(p*2,l,r),query(p*2+1,l,r));//横跨中点
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	//print(1);
	for(int i=1;i<=m;i++){
		opt=get();
		if(opt=='C'){
			x=read(),v=read();
			update(1,x,v);
		}
		else{
			l=read(),r=read();
			printf("%d\n",query(1,l,r));
		}
	}
	return 0;
}

3.单点修改,区间查询最大连续子段和

#include<bits/stdc++.h>
using namespace std;
const int MAXN=500000+10;
int k;//类型符,2修改,1查询 
int x,v,l,r;//C指把a[x]改为v,Q指询问区间[l,r]上的区间最大值 
int n,m,a[MAXN];
inline int read(){//快读int
	int x=0,f=1;char ch=getchar();
	if(!isdigit(ch)){if(ch==45)f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct SegmentTree{
	int l,r;//左右端点 
	int sum;//区间和 
	int dat;//区间最大连续子段和 
	int lmax;//紧靠左端的最大连续子段和 
	int rmax;//紧靠右端的最大连续子段和 
}tree[MAXN*4];
void build(int p,int l,int r){
	tree[p].l=l;tree[p].r=r;//确定树的左右端点 
	if(l==r){tree[p].sum=tree[p].dat=tree[p].lmax=tree[p].rmax=a[l];return ;}//如果左右相等,那么确定附加信息并返回
	int mid=(l+r)/2;
	build(p*2,l,mid);//递归构造左子树 
	build(p*2+1,mid+1,r);//递归构造右子树 
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//向上更新信息,pushup
	tree[p].lmax=max(tree[p*2].lmax,tree[p*2].sum+tree[p*2+1].lmax);
	tree[p].rmax=max(tree[p*2+1].rmax,tree[p*2+1].sum+tree[p*2].rmax);
	tree[p].dat=max(tree[p*2].dat,max(tree[p*2+1].dat,tree[p*2].rmax+tree[p*2+1].lmax));
}
void print(int p){//打印线段树,可用于调试 
	printf("%d %d %d %d %d %d %d\n",p,tree[p].l,tree[p].r,tree[p].sum,tree[p].dat,tree[p].lmax,tree[p].rmax);//输出对应信息
	if(tree[p].l==tree[p].r)return ;
	print(p*2);
	print(p*2+1);
}
void update(int p,int x,int v){//单点修改,将a[x]修改为v 
	if(tree[p].l==tree[p].r) {tree[p].sum=tree[p].dat=tree[p].lmax=tree[p].rmax=v;return;}//找到目标,直接更新 
	//折半查找(修改点x不在左半就在右半) 
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点 
	if(x<=mid)update(p*2,x,v);//递归更新左半
	else update(p*2+1,x,v);//递归更新右半
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//向上更新信息,pushup
	tree[p].lmax=max(tree[p*2].lmax,tree[p*2].sum+tree[p*2+1].lmax);
	tree[p].rmax=max(tree[p*2+1].rmax,tree[p*2+1].sum+tree[p*2].rmax);
	tree[p].dat=max(tree[p*2].dat,max(tree[p*2+1].dat,tree[p*2].rmax+tree[p*2+1].lmax)); 
}
SegmentTree query(int p,int l,int r){//返回区间[l,r]的最大子段和 
	//若目标区间完全包含节点p代表的区间,直接返回区间值
	if(l<=tree[p].l&&tree[p].r<=r)return tree[p];
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点
	if(r<=mid)return query(p*2,l,r);//完全在左半 
	else if(l>mid)return query(p*2+1,l,r);//完全在右半 
	else{//横跨中点
		SegmentTree a,b,c;
		a=query(p*2,l,r);
		b=query(p*2+1,l,r);
		c.sum=a.sum+b.sum;
		c.lmax=max(a.lmax,a.sum+b.lmax);
		c.rmax=max(b.rmax,b.sum+a.rmax);
		c.dat=max(a.dat,max(b.dat,a.rmax+b.lmax));
		return c;
	} 
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	//print(1);
	for(int i=1;i<=m;i++){
		k=read();
		if(k==2){
			x=read(),v=read();
			update(1,x,v);
		}
		else{
			l=read(),r=read();
			if(l>r)swap(l,r);
			printf("%d\n",query(1,l,r).dat);
		}
	}
	return 0;
}

4.区间修改,区间查询区间和(暴力)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
char opt;//类型符,C修改,Q查询 
int l,r,d;//C指把a[l]~a[r]都加上d,Q指询问区间[l,r]上的区间和 
int n,m,a[MAXN];
inline int read(){//快读int
	int x=0,f=1;char ch=getchar();
	if(!isdigit(ch)){if(ch==45)f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
char get(){//快读char 
	char ch=getchar();
	while(isdigit(ch))ch=getchar();
	return ch; 
}
struct SegmentTree{
	int l,r;//左右端点 
	int sum;//附加信息,如区间和
}tree[MAXN*4];
void build(int p,int l,int r){
	tree[p].l=l;tree[p].r=r;//确定树的左右端点 
	if(l==r){tree[p].sum=a[l];return ;}//如果左右相等,那么确定附加信息并返回
	int mid=(l+r)/2;
	build(p*2,l,mid);//递归构造左子树 
	build(p*2+1,mid+1,r);//递归构造右子树 
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//向上更新信息,pushup  
}
void print(int p){//打印线段树,可用于调试 
	printf("%d %d %d %d\n",p,tree[p].l,tree[p].r,tree[p].sum);//输出对应信息
	if(tree[p].l==tree[p].r)return ;
	print(p*2);
	print(p*2+1);
}
void add(int p,int d){
	//tree[p].sum+=d*(tree[p].r-tree[p].l+1);
	//进入时完成,此时要注释掉pushup 
	if(tree[p].l==tree[p].r){tree[p].sum+=d/*如果进入时完成,那么前面这句删去*/;return;}
	add(p*2,d);
	add(p*2+1,d);
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//pushup 
}
void update(int p,int l,int r,int d){//区间修改,将a[l]~a[r]都加上d 
	if(tree[p].l==tree[p].r) {add(p,d);return;}//找到目标,直接更新 
	//折半查找(修改点x不在左半就在右半) 
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点 
	if(r<=mid)update(p*2,l,r,d);//递归更新左半
	else if(l>mid)update(p*2+1,l,r,d);//递归更新右半
	else update(p*2,l,r,d),update(p*2+1,l,r,d);//更新两半
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//pushup
}
int query(int p,int l,int r){//返回区间[l,r]的区间和 
	//若目标区间完全包含节点p代表的区间,直接返回区间值
	if(l<=tree[p].l&&tree[p].r<=r)return tree[p].sum;
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点
	/*int val=0;
	if(l<=mid)val+=query(p*2,l,r);
	if(r>mid)val+=query(p*2+1,l,r);
	return val;*///版本1
	if(r<=mid)return query(p*2,l,r);//完全在左半 
	else if(l>mid)return query(p*2+1,l,r);//完全在右半 
	else return query(p*2,l,r)+query(p*2+1,l,r);//横跨中点
	//版本2 
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	//print(1);
	for(int i=1;i<=m;i++){
		opt=get();
		if(opt=='C'){
			l=read(),r=read(),d=read();
			update(1,l,r,d);
		}
		else{
			l=read(),r=read();
			printf("%d\n",query(1,l,r));
		}
	}
	return 0;
}

5.区间修改,区间查询区间和(懒标记)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
char opt;//类型符,C修改,Q查询 
int l,r,d;//C指把a[l]~a[r]都加上d,Q指询问区间[l,r]上的区间和 
int n,m,a[MAXN];
inline int read(){//快读int
	int x=0,f=1;char ch=getchar();
	if(!isdigit(ch)){if(ch==45)f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
char get(){//快读char 
	char ch=getchar();
	while(isdigit(ch))ch=getchar();
	return ch; 
}
struct SegmentTree{
	int l,r;//左右端点 
	int sum;//附加信息,如区间和
	int add;//懒标记 
}tree[MAXN*4];
void build(int p,int l,int r){
	tree[p].l=l;tree[p].r=r;//确定树的左右端点 
	if(l==r){tree[p].sum=a[l];return ;}//如果左右相等,那么确定附加信息并返回
	int mid=(l+r)/2;
	build(p*2,l,mid);//递归构造左子树 
	build(p*2+1,mid+1,r);//递归构造右子树 
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//向上更新信息,pushup  
}
void print(int p){//打印线段树,可用于调试 
	printf("%d %d %d %d %d\n",p,tree[p].l,tree[p].r,tree[p].sum,tree[p].add);//输出对应信息
	if(tree[p].l==tree[p].r)return ;
	print(p*2);
	print(p*2+1);
}
void pushdown(int p){
	if(tree[p].add){//节点p有懒标记 
		tree[p*2].sum+=tree[p].add*(tree[p*2].r-tree[p*2].l+1);//更新左右子树的信息 
		tree[p*2+1].sum+=tree[p].add*(tree[p*2+1].r-tree[p*2+1].l+1);
		//为两个子树增加懒标记 
		tree[p*2].add+=tree[p].add;
		tree[p*2+1].add+=tree[p].add;
		tree[p].add=0;//清除懒标记 
	}
} 
void update(int p,int l,int r,int d){//区间修改,将a[l]~a[r]都加上d 
	if(l<=tree[p].l&&tree[p].r<=r) {
		tree[p].sum+=d*(tree[p].r-tree[p].l+1);
		tree[p].add+=d;//增加懒标记 
		return;
	}//找到目标,直接更新 
	//折半查找(修改点x不在左半就在右半)
	pushdown(p);//下传懒标记 
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点 
	if(l<=mid)update(p*2,l,r,d);//递归更新左半
	if(r>mid)update(p*2+1,l,r,d);//递归更新右半
	tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;//pushup
}
int query(int p,int l,int r){//返回区间[l,r]的区间和 
	//若目标区间完全包含节点p代表的区间,直接返回区间值
	if(l<=tree[p].l&&tree[p].r<=r)return tree[p].sum;
	pushdown(p);//下传懒标记 
	int mid=(tree[p].l+tree[p].r)/2;//求区间中点
	/*int val=0;
	if(l<=mid)val+=query(p*2,l,r);
	if(r>mid)val+=query(p*2+1,l,r);
	return val;*///版本1
	if(r<=mid)return query(p*2,l,r);//完全在左半 
	else if(l>mid)return query(p*2+1,l,r);//完全在右半 
	else return query(p*2,l,r)+query(p*2+1,l,r);//横跨中点
	//版本2 
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	//print(1);
	for(int i=1;i<=m;i++){
		opt=get();
		if(opt=='C'){
			l=read(),r=read(),d=read();
			update(1,l,r,d);
		}
		else{
			l=read(),r=read();
			printf("%d\n",query(1,l,r));
		}
	}
	return 0;
}

例题精选:

1.P1471

来源:P1471
题解:见[题解]P1471

2.CF438D

来源:CF438D
题解:见[题解]CF438D

3.CF145E

来源:CF145E
题解:见题解

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值