线段树模板

单点修改,区间查询

区间修改,区间查询(利用极长线段覆盖的思想)

复杂度为O(logn)

建树的复杂度为o(n),因为节点数有2n-1个。

#include <bits/stdc++.h> //线段树->进行递归 
using namespace std;
#define N 500050
/*#define N 500050
int a[N];
struct segmentTree{
	struct Node{
		int l,r;
		long long sum;//做的是区间求和 有2n-1个节点 开4n个节点即可 
	}node[N<<2];
}seg;
void build(int i,int l,int r){
	seg.node[i].l=l,seg.node[i].r=r;
	if(l==r){
		seg.node[i].sum=a[l];
		return;
	}
	
	int mid=(l+r)/2;
	build(i*2,l,mid);
	build(i*2+1,mid+1,r);
    seg.node[i].sum=seg.node[i*2].sum+seg.node[i*2+1].sum;
	
}
*/
struct SegmentTree2{
	#define ls (i<<1)
	#define rs (ls|1)
	#define mid (l+r>>1)
	long long sum[N<<2],tag[N<<2]; 
	void build(int i,int l, int r){
	    tag[i]=0;//置零,初始化 
		if(l==r){
		 scanf("%d",&sum[i]);
		 
		}
		else{
			build(ls,l,mid),build(rs,mid+1,r);
			sum[i]=sum[ls]+sum[rs];
		}
	}
	void add(int i,int l,int r,int x,int k){
		sum[i]+=k;
		if(l==r) return;
		if(x<=mid) add(ls,l,mid,x,k);
		else add(rs,mid+1,r,x,k);
		
	} //动态开点就再封装一层 
	void pushdown(int i,int l,int r){
		if(tag[i]==0) return;
		sum[ls]+=tag[i]*(mid-l+1),sum[rs]+=tag[i]*(r-mid);
		tag[ls]+=tag[i],tag[rs]+=tag[i];
		tag[i]=0;
		
	}
	void add1(int i,int l,int r,int L,int R,long long k){ //o(n) 递归到每一片叶子)
	
		if(l>R||r<L) return;
		/*if(l==r){
					sum[i]+=k;
					return;
				} 
				add1(ls,l,mid,L,R,k),add1(rs,mid+1,r,L,R,k);
			
				sum[i]=sum[ls]+sum[rs];*/
	   else if(L<=l&&r<=R){
	   	sum[i]+=k*(r-l+1),tag[i]+=k;
	   }
	   else{
	   	pushdown(i,l,r);
	   	add1(ls,l,mid,L,R,k);
	   	add1(rs,mid+1,r,L,R,k);
	   	sum[i]=sum[ls]+sum[rs];//可以封装成update  
	   }
				
		
	}
	
   long long query(int i,int l,int r,int L,int R){
   	if(L<=l&&r<=R) return sum[i];
   	else if(l>R||r<L) return 0;
   	//else return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
   	else{
	   	pushdown(i,l,r);
	   	return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
	   }
   } 
   
}seg1;

// 单点修改 a[x]+k 只影响一条链上的含x的链 o(logn)
//查询  [l,r]用极长的线段去覆盖,每层4个,logn层,o(logn)
 int n,m;
  
int main(){
	
	scanf("%d%d",&n,&m);  
    seg1.build(1,1,n);
    while(m--){
		int op,a,b;
		scanf("%d %d %d",&op,&a,&b);
		if(op==1) seg1.add(1,1,n,a,b);
		else printf("%lld\n",seg1.query(1,1,n,a,b));
		
	}
	 
	return 0;
} 
//懒标记 对于一次区间修改,极长线段打上懒标记
//为了保证子节点的信息正确,把懒标记下推给儿子节点,清空当前节点的标记
//另一个策略,永久化标记

//区间修改,区间查询  
 

最最基本的板子了,视频参见2022寒假培训 线段树(一)_哔哩哔哩_bilibili

从这里慢慢补起吧,陌生人~

#include <bits/stdc++.h>
using namespace std;
#define N 100000
struct segmentTree{
#define lr (i<<1)
#define rr (lr|1)
#define mid  (lr+rr>>1)
long long sum[N<<2],tag[N<<2];

void build(int i,int l,int r){
	tag[i]=0;//置零 
	if(l==r) scanf("%d",&sum[i]);
	
	else{
		build(lr,l,mid);
		build(rr,mid+1,r);
		sum[i]=sum[lr]+sum[rr];
		
		
	}	
}
void add(int i,int l,int r,int x,int k){
	   sum[i]+=k;
	   if(l==r) return;
	   if(x<=mid) add(lr,l,mid,x,k);
	   else{
	   	add(rr,mid+1,r,x,k);
	   }
		
	}
	void pushdown(int i,int l,int r){
		if(tag[i]==0) return;
		sum[lr]+=tag[i]*(mid-l+1),sum[rr]+=tag[i]*(r-mid);
		tag[lr]+=tag[i],tag[rr]+=tag[i];
		tag[i]=0;//取消标记 
	}
void add1(int i,int l,int r,int L,int R,int k){
	if(l>R||r<L) return;
	else if(L<=l&&r<=R){
		sum[i]+=k*(r-l+1),tag[i]+=k;
		
	}
	else{
		pushdown(1,l,r);
		add1(lr,l,mid,L,R,k);
		add1(rr,mid+1,r,L,R,k);
		sum[i]=sum[rr]+sum[lr];
	}
} 
long long query(int i,int l,int r,int L,int R){
	if(L<=l&&r<=R) return sum[i];
	else if(l>R||r<L) return 0;
	else return query(lr,l,mid,L,R)+query(rr,mid+1,r,L,R);
	 
}
long long query1(int i,int l,int r,int L,int R){
	if(L<=l&&r<=R) return sum[i];
	else if(l>R||r<L) return 0;
	else{
		 pushdown(i,l,r);
		return	query(lr,l,mid,L,R)+query(rr,mid+1,r,L,R);
	} 
	 
}
	
}seg;
int n,m;
int main(){
scanf("%d%d",&n,&m);
seg.build(1,1,n);
while(m--){
	int op,a,b;
	scanf("%d %d %d",&op,&a,&b);
	if(op==1) seg.add(1,1,n,a,b);
	else printf("%lld\n",seg.query(1,1,n,a,b));
	
}	return 0;

}

用到单点查询的情况不多,但是一般用到的话,封装左右端点比较容易。上面的板子是用附加传参代替了左右端点。

#include<iostream>
#include<cstdio>
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
#define update(rt) t[rt].data=t[ls].data+t[rs].data
#define N 500005
//宏定义大法好

using namespace std;

struct tree{
	ll tag,data;
	int left,right;
}t[(N<<4)+5];//之前因为数组开小了,RE了3个点

int n,m,pos;
ll s[N],ans;
int a,b,x,k;

inline void build(int rt,int l,int r){
//递归建树
	t[rt].left=l;t[rt].right=r;t[rt].tag=0;
	if(l==r){
		t[rt].data=s[l];
		return ;
	}
	build(ls,l,mid);
	build(rs,mid+1,r);
	update(rt);
	return ;
}

inline void pushdown(int rt){//标记下传
	t[ls].tag+=t[rt].tag;
	t[rs].tag+=t[rt].tag;
	t[ls].data+=t[rt].tag*(t[ls].right-t[ls].left+1);
	t[rs].data+=t[rt].tag*(t[rs].right-t[rs].left+1);
	t[rt].tag=0;
	return ;
}

inline void addval(int rt){
//区间修改
	int l=t[rt].left,r=t[rt].right;
	if(a<=l&&r<=b){
		t[rt].tag+=x;
		t[rt].data+=(t[rt].right-t[rt].left+1)*x;
		return ;
	}
	if(t[rt].tag) pushdown(rt);
	if(a<=mid) addval(ls);
	if(b>mid) addval(rs);
	update(rt);
	return ;
}

inline void query(int rt){
//单点查询
	a=pos,b=pos;
	int l=t[rt].left,r=t[rt].right;
	if(a<=l&&r<=b){
		ans+=t[rt].data;
		return ;
	}
	if(t[rt].tag) pushdown(rt);
	if(a<=mid) query(ls);
	if(b>mid) query(rs);
	return ;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%lld",&s[i]);
	build(1,1,n);
	for(int i=1;i<=m;++i){
		scanf("%d",&k);
		if(k==1){
			scanf("%d%d%d",&a,&b,&x);
			addval(1);//区间修改
		}
		else{
			scanf("%d",&pos);//要查询的位置
			query(1);
			printf("%lld\n",ans);
			ans=0;
		}
	}
	return 0;
}

洛谷的线段树二

#include <bits/stdc++.h>
using namespace std;
#define N 100010
int n,m,p;
// a[j]*mul[ls]+add[ls]
//( a[j]*mul[ls]+add[ls])*mul[i]+add[i]    a[j]*mul[ls]*mul[i]+add[ls]*mul[i]+add[i];
struct segmentTree{
	#define ls (i<<1)
	#define rs (ls|1)
	#define mid  (l+r>>1)
	int sum[N<<2],add[N<<2],mul[N<<2];
	void build(int i,int l,int r){
		add[i]=0,mul[i]=1;
		if(l==r) scanf("%d",&sum[i]),sum[i]%=p;
		else{
			build(ls,l,mid),build(rs,mid+1,r);
			sum[i]=(sum[ls]+sum[rs])%p;
		}
	}
	void pushdown(int i,int l,int r){
		//分三步
		mul[ls]=1ll*mul[ls]*mul[i]%p;
		mul[rs]=1ll*mul[rs]*mul[i]%p;
		add[ls]=(1ll*add[ls]*mul[i]%p+add[i])%p;
		add[rs]=(1ll*add[rs]*mul[i]%p+add[i])%p;
		sum[ls]=(1ll*sum[ls]*mul[i]%p+1ll*(mid-l+1)*add[i]%p)%p;
		sum[rs]=(1ll*sum[rs]*mul[i]%p+1ll*(r-mid)*add[i]%p)%p;
		add[i]=0,mul[i]=1;
		 
	}
	void Mul(int i,int l,int r,int L,int R,int k){
		if(l>R||r<L) return;
		else if(L<=l&&r<=R){
			sum[i]=1ll*sum[i]*k%p;
			mul[i]=1ll*mul[i]*k%p;
			add[i]=1ll*add[i]*k%p;
			
		}
		else{
			pushdown(i,l,r);
			Mul(ls,l,mid,L,R,k);
			Mul(rs,mid+1,r,L,R,k);
			sum[i]=(sum[ls]+sum[rs])%p;
			
		}
	}
	void Add(int i,int l,int r,int L,int R,int k){
		if(l>R||r<L) return;
		else if(L<=l&&r<=R){
			sum[i]=(sum[i]+1ll*(r-l+1)*k%p)%p;
			add[i]=(add[i]+k)%p;
		}
		else{
			pushdown(i,l,r);
			Add(ls,l,mid,L,R,k);
			Add(rs,mid+1,r,L,R,k);
			sum[i]=(sum[ls]+sum[rs])%p;
			
		}
	}
	long long query(int i,int l,int r,int L,int R){
		if(L<=l&&r<=R) return sum[i];
		else if(l>R||r<L) return 0;
		else{
    pushdown(i,l,r);
    return (query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R))%p;
    
		} 
	}
	
}seg;

int main(){
	scanf("%d%d%d",&n,&m,&p);
	seg.build(1,1,n);
	while(m--){
		int op,a,b,c;
		scanf("%d%d%d",&op,&a,&b);
		if(op!=3) scanf("%d",&c);
		if(op==1) seg.Mul(1,1,n,a,b,c);
		else if(op==2) seg.Add(1,1,n,a,b,c);
		else printf("%d\n",seg.query(1,1,n,a,b));
		
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM线段模板C ```c #include<bits/stdc++.h> using namespace std; const int N=1e5+5;//数组开大一点 int n,m; int a[N]; struct node{ int l,r;//左右端点 int sum,lazy;//区间和和懒标记 }t[N*4]; void up(int p){//向上更新 t[p].sum=t[p<<1].sum+t[p<<1|1].sum; } void down(int p){//向下更新 if(t[p].lazy){ t[p<<1].lazy+=t[p].lazy; t[p<<1|1].lazy+=t[p].lazy; t[p<<1].sum+=t[p].lazy*(t[p<<1].r-t[p<<1].l+1); t[p<<1|1].sum+=t[p].lazy*(t[p<<1|1].r-t[p<<1|1].l+1); t[p].lazy=0; } } void build(int p,int l,int r){//建 t[p].l=l,t[p].r=r; if(l==r){ t[p].sum=a[l]; return; } int mid=(l+r)/2; build(p<<1,l,mid); build(p<<1|1,mid+1,r); up(p); } void change(int p,int l,int r,int k){//单点修改 if(t[p].l==t[p].r){ t[p].sum=k; return; } down(p); int mid=(t[p].l+t[p].r)/2; if(r<=mid) change(p<<1,l,r,k); else if(l>mid) change(p<<1|1,l,r,k); else{ change(p<<1,l,mid,k); change(p<<1|1,mid+1,r,k); } up(p); } void add(int p,int l,int r,int k){//区间修改 if(t[p].l>=l&&t[p].r<=r){ t[p].sum+=k*(t[p].r-t[p].l+1); t[p].lazy+=k; return; } down(p); int mid=(t[p].l+t[p].r)/2; if(l<=mid) add(p<<1,l,r,k); if(r>mid) add(p<<1|1,l,r,k); up(p); } int query(int p,int l,int r){//区间查询 if(t[p].l>=l&&t[p].r<=r) return t[p].sum; down(p); int mid=(t[p].l+t[p].r)/2,ans=0; if(l<=mid) ans+=query(p<<1,l,r); if(r>mid) ans+=query(p<<1|1,l,r); return ans; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; build(1,1,n); for(int i=1;i<=m;i++){ int opt,x,y,k; cin>>opt; if(opt==1){ cin>>x>>y>>k; add(1,x,y,k); } if(opt==2){ cin>>x>>y; cout<<query(1,x,y)<<endl; } if(opt==3){ cin>>x>>k; change(1,x,x,k); } } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值