线段树笔记

代码源数据结构中级课

第二课:线段树

单点修改+区间查询最值:

struct node{
	int val;
}seg[N*4];
void update(int id){
	seg[id].val=min(seg[id*2].val,seg[id*2+1].val);
}
void build(int id,int l,int r){
	if(l==r) seg[id].val=a[l];
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		update(id);
	}
}
void change(int id,int l,int r,int pos,int valx){
	if(l==r) seg[id].val=valx;
	else{
		int mid=(l+r)>>1;
		if(pos<=mid) change(id*2,l,mid,pos,valx);
		else change(id*2+1,mid+1,r,pos,valx);
		update(id);
	}
}
int query(int id,int l,int r,int ql,int qr){
	if(l==ql&&r==qr) return seg[id].val;
	int mid=(l+r)>>1;
	if(qr<=mid) return query(id*2,l,mid,ql,qr);
	else if(ql>mid) return query(id*2+1,mid+1,r,ql,qr);
	else return min(query(id*2,l,mid,ql,mid),query(id*2+1,mid+1,r,mid+1,qr));
}

线段树1

在这里插入图片描述

思路:单点修改+区间查询
把查询的最小值和最小值出现的次数封装起来,重载 + + +运算符即可套用上面模板解决该题

struct info{
	ll minv,mincnt;
};
info operator +(const info &l,const info &r){
	info ans;
	ans.minv=min(l.minv,r.minv);
	if(l.minv==r.minv) ans.mincnt=l.mincnt+r.mincnt;
	else if(l.minv>r.minv) ans.mincnt=r.mincnt;
	else ans.mincnt=l.mincnt;
	return ans;
}
struct node{
	info val;
}seg[N*4];

C o d e : Code: Code

#include <bits/stdc++.h>
using namespace std;
#define endl '\n';
typedef long long ll;
typedef unsigned long long u64;
typedef pair<string,int> PII;
const int N=2e5+10,mod=998244353;
template<class T>
void read(T &x){
	x=0; char c; int sign=1;
	do{ c=getchar(); if(c=='-') sign=-1;}while(!isdigit(c));
	do{ x=x*10+c-'0',c=getchar();}while(isdigit(c));
	x*=sign;
}
int n,q,a[N];
struct info{
	ll minv,mincnt;
};
info operator +(const info &l,const info &r){
	info ans;
	ans.minv=min(l.minv,r.minv);
	if(l.minv==r.minv) ans.mincnt=l.mincnt+r.mincnt;
	else if(l.minv>r.minv) ans.mincnt=r.mincnt;
	else ans.mincnt=l.mincnt;
	return ans;
}
struct node{
	info val;
}seg[N*4];
void update(int id){
	seg[id].val=seg[id*2].val+seg[id*2+1].val;
}
void build(int id,int l,int r){
	if(l==r) seg[id].val={a[l],1};
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		update(id);
	}
}
void change(int id,int l,int r,int pos,int valx){
	if(l==r) seg[id].val={valx,1};
	else{
		int mid=(l+r)>>1;
		if(pos<=mid) change(id*2,l,mid,pos,valx);
		else change(id*2+1,mid+1,r,pos,valx);
		update(id);
	}
}
info query(int id,int l,int r,int ql,int qr){
	if(l==ql&&r==qr) return seg[id].val;
	int mid=(l+r)>>1;
	if(qr<=mid) return query(id*2,l,mid,ql,qr);
	else if(ql>mid) return query(id*2+1,mid+1,r,ql,qr);
	else return query(id*2,l,mid,ql,mid)+query(id*2+1,mid+1,r,mid+1,qr);
}
int main(){
	read(n),read(q);
	for(int i=1;i<=n;i++) read(a[i]);
	build(1,1,n);
	while(q--){
		int op;read(op);
		if(op==1){
			int x,d;read(x),read(d);
			change(1,1,n,x,d);
		}else{
			int l,r;read(l),read(r);
			info ans=query(1,1,n,l,r);
			printf("%lld %lld\n",ans.minv,ans.mincnt);
		}
	}
	return 0;
}

线段树2

在这里插入图片描述

思路:复杂信息的合并
求区间 [ l , r ] [l,r] [l,r]中的最大子段和,显然我们需要维护的信息有:区间的最大子段和 m m s mms mms,区间的最大前缀和 m p r e mpre mpre,区间的最大后缀和 m s u f msuf msuf,区间和 s s s
考虑一下在区间合并的时候需要考虑到的信息合并有:
区间的最大子段和为 m a x max max :左最大子段和,右最大子段和,左最大后缀和+右最大前缀和
区间最大前缀和为 m a x max max:左最大前缀和,左区间和+右最大前缀和
区间最大后缀和为 m a x max max:右最大后缀和,右区间和+左最大后缀和
区间和为:左区间和+右区间和
将这些信息封装起来,即:

struct info{
	ll mss,mpre,msuf,s;
	info(){}
	info(int a):mss(a),mpre(a),msuf(a),s(a){}
};
info operator +(const info &l,const info &r){
	info ans;
	ans.mss=max({l.mss,r.mss,l.msuf+r.mpre});
	ans.mpre=max(l.mpre,l.s+r.mpre);
	ans.msuf=max(r.msuf,r.s+l.msuf);
	ans.s=l.s+r.s;
	return ans;
}
struct node{
	info val;
}seg[N*4];

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n';
typedef long long ll;
typedef unsigned long long u64;
typedef pair<string,int> PII;
const int N=2e5+10,mod=998244353;
template<class T>
void read(T &x){
	x=0; char c; int sign=1;
	do{ c=getchar(); if(c=='-') sign=-1;}while(!isdigit(c));
	do{ x=x*10+c-'0',c=getchar();}while(isdigit(c));
	x*=sign;
}
int n,q,a[N];
struct info{
	ll mss,mpre,msuf,s;
	info(){}
	info(int a):mss(a),mpre(a),msuf(a),s(a){}
};
info operator +(const info &l,const info &r){
	info ans;
	ans.mss=max({l.mss,r.mss,l.msuf+r.mpre});
	ans.mpre=max(l.mpre,l.s+r.mpre);
	ans.msuf=max(r.msuf,r.s+l.msuf);
	ans.s=l.s+r.s;
	return ans;
}
struct node{
	info val;
}seg[N*4];
void update(int id){
	seg[id].val=seg[id*2].val+seg[id*2+1].val;
}
void build(int id,int l,int r){
	if(l==r) seg[id].val=info(a[l]);
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		update(id);
	}
}
void change(int id,int l,int r,int pos,int valx){
	if(l==r) seg[id].val=info(valx);
	else{
		int mid=(l+r)>>1;
		if(pos<=mid) change(id*2,l,mid,pos,valx);
		else change(id*2+1,mid+1,r,pos,valx);
		update(id);
	}
}
info query(int id,int l,int r,int ql,int qr){
	if(l==ql&&r==qr) return seg[id].val;
	int mid=(l+r)>>1;
	if(qr<=mid) return query(id*2,l,mid,ql,qr);
	else if(ql>mid) return query(id*2+1,mid+1,r,ql,qr);
	else return query(id*2,l,mid,ql,mid)+query(id*2+1,mid+1,r,mid+1,qr);
}
int main(){
	read(n),read(q);
	for(int i=1;i<=n;i++) read(a[i]);
	build(1,1,n);
	while(q--){
		int op;read(op);
		if(op==1){
			int x,d;read(x),read(d);
			change(1,1,n,x,d);
		}else{
			int l,r;read(l),read(r);
			info ans=query(1,1,n,l,r);
			printf("%lld\n",ans.mss);
		}
	}
	return 0;
}

线段树打标记1

在这里插入图片描述

思路:区间打标记以及下传
区间修改+区间最值查询

#include <bits/stdc++.h>
using namespace std;
#define endl '\n';
typedef long long ll;
typedef unsigned long long u64;
typedef pair<string,int> PII;
const int N=2e5+10,mod=998244353;
template<class T>
void read(T &x){
	x=0; char c; int sign=1;
	do{ c=getchar(); if(c=='-') sign=-1;}while(!isdigit(c));
	do{ x=x*10+c-'0',c=getchar();}while(isdigit(c));
	x*=sign;
}
int n,q,a[N];
struct node{
	ll val,t;
}seg[N*4];
void update(int id){
	seg[id].val=max(seg[id*2].val,seg[id*2+1].val);
}
void settag(int id,ll tx){
	seg[id].val=seg[id].val+tx;
	seg[id].t=seg[id].t+tx;
}
void pushdown(int id){
	if(seg[id].t){
		settag(id*2,seg[id].t);
		settag(id*2+1,seg[id].t);
		seg[id].t=0;
	}
}
void build(int id,int l,int r){
	if(l==r) seg[id].val=a[l];
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		update(id);
	}
}
void modify(int id,int l,int r,int ql,int qr,int valx){
	if(l==ql&&r==qr){
		settag(id,valx);
		return;
	}	
	int mid=(l+r)>>1;
	pushdown(id);
	if(qr<=mid) modify(id*2,l,mid,ql,qr,valx);
	else if(ql>mid) modify(id*2+1,mid+1,r,ql,qr,valx);
	else{
		modify(id*2,l,mid,ql,mid,valx);
		modify(id*2+1,mid+1,r,mid+1,qr,valx);
	}
	update(id);
}
ll query(int id,int l,int r,int ql,int qr){
	if(l==ql&&r==qr) return seg[id].val;
	int mid=(l+r)>>1;
	pushdown(id);
	if(qr<=mid) return query(id*2,l,mid,ql,qr);
	else if(ql>mid) return query(id*2+1,mid+1,r,ql,qr);
	else return max(query(id*2,l,mid,ql,mid),query(id*2+1,mid+1,r,mid+1,qr));
}
int main(){
	read(n),read(q);
	for(int i=1;i<=n;i++) read(a[i]);
	build(1,1,n);
	while(q--){
		int op;read(op);
		if(op==1){
			int l,r,d;read(l),read(r),read(d);
			modify(1,1,n,l,r,d);
		}else{
			int l,r;read(l),read(r);
			ll ans=query(1,1,n,l,r);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

线段树打标记2

在这里插入图片描述

思路:复杂的标记问题,标记的顺序
对于操作 1 , 2 , 3 1,2,3 1,2,3可以统一抽象为:维护一个 t a g ( m u l , a d d ) tag(mul,add) tag(mul,add)
操作 1 1 1 m u l = 0 , a d d = d mul=0,add=d mul=0,add=d
操作 2 2 2 m u l = d , a d d = 0 mul=d,add=0 mul=d,add=0
操作 3 3 3 m u l = 0 , a d d = d mul=0,add=d mul=0,add=d
接下来考虑:两个标记的顺序对结果的影响, t a g : t 1 , t 2 tag:t1,t2 tag:t1,t2
x ∗ t 1 + t 2 x*t_1+t_2 xt1+t2 ( x ∗ t 1 . m u l + t 1 . a d d ) ∗ t 2 . m u l + t 2 . a d d (x*t_1.mul+t_1.add)*t_2.mul+t_2.add (xt1.mul+t1.add)t2.mul+t2.add
结果即为: t 1 . m u l ∗ t 2 . m u l , t 1 . a d d ∗ t 2 . m u l + t 2 . a d d {t_1.mul*t_2.mul,t_1.add*t_2.mul+t_2.add} t1.mult2.mul,t1.addt2.mul+t2.add

struct tag{
	ll mul,add;
};
tag operator+(const tag &t1,const tag &t2){
	tag ans;
	ans.mul=t1.mul*t2.mul%mod;
	ans.add=(t1.add*t2.mul%mod+t2.add)%mod;
	return ans;
}

对于一个 t a g : t x tag:tx tag:tx加到线段树某节点中时, v a l val val的改变为 v a l ∗ t x . m u l + s i z e ∗ t x . a d d val*tx.mul+size*tx.add valtx.mul+sizetx.add

void settag(int id,tag tx){
	seg[id].val=(seg[id].val*tx.mul+seg[id].sz*tx.add)%mod;
	seg[id].t=seg[id].t+tx;
}

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n';
typedef long long ll;
typedef unsigned long long u64;
typedef pair<string,int> PII;
const int N=2e5+10,mod=1e9+7;
template<class T>
void read(T &x){
	x=0; char c; int sign=1;
	do{ c=getchar(); if(c=='-') sign=-1;}while(!isdigit(c));
	do{ x=x*10+c-'0',c=getchar();}while(isdigit(c));
	x*=sign;
}
int n,q,a[N];
struct tag{
	ll mul,add;
};
tag operator+(const tag &t1,const tag &t2){
	tag ans;
	ans.mul=t1.mul*t2.mul%mod;
	ans.add=(t1.add*t2.mul%mod+t2.add)%mod;
	return ans;
}
struct node{
	tag t;
	ll val;
	int sz;
}seg[N*4];
void update(int id){
	seg[id].val=(seg[id*2].val+seg[id*2+1].val)%mod;
}
void settag(int id,tag tx){
	seg[id].val=(seg[id].val*tx.mul+seg[id].sz*tx.add)%mod;
	seg[id].t=seg[id].t+tx;
}
void pushdown(int id){
	if(seg[id].t.mul!=1||seg[id].t.add!=0){
		settag(id*2,seg[id].t);
		settag(id*2+1,seg[id].t);
		seg[id].t.add=0;
		seg[id].t.mul=1;
	}
}
void build(int id,int l,int r){
	seg[id].sz=r-l+1;
	seg[id].t={1,0};
	if(l==r) seg[id].val=a[l];
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		update(id);
	}
}
void modify(int id,int l,int r,int ql,int qr,tag valx){
	if(l==ql&&r==qr){
		settag(id,valx);
		return;
	}	
	int mid=(l+r)>>1;
	pushdown(id);
	if(qr<=mid) modify(id*2,l,mid,ql,qr,valx);
	else if(ql>mid) modify(id*2+1,mid+1,r,ql,qr,valx);
	else{
		modify(id*2,l,mid,ql,mid,valx);
		modify(id*2+1,mid+1,r,mid+1,qr,valx);
	}
	update(id);
}
ll query(int id,int l,int r,int ql,int qr){
	if(l==ql&&r==qr) return seg[id].val;
	int mid=(l+r)>>1;
	pushdown(id);
	if(qr<=mid) return query(id*2,l,mid,ql,qr);
	else if(ql>mid) return query(id*2+1,mid+1,r,ql,qr);
	else return (query(id*2,l,mid,ql,mid)+query(id*2+1,mid+1,r,mid+1,qr))%mod;
}
int main(){
	read(n),read(q);
	for(int i=1;i<=n;i++) read(a[i]);
	build(1,1,n);
	while(q--){
		int op;read(op);
		if(op==1){
			int l,r,d;read(l),read(r),read(d);
			modify(1,1,n,l,r,{1,d});
		}else if(op==2){
			int l,r,d;read(l),read(r),read(d);
			modify(1,1,n,l,r,{d,0});
		}else if(op==3){
			int l,r,d;read(l),read(r),read(d);
			modify(1,1,n,l,r,{0,d});
		}else{
			int l,r;read(l),read(r);
			ll ans=query(1,1,n,l,r);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

线段树上二分

在这里插入图片描述

思路:模板,注意写好 s e a r c h search search函数,先判断左子区间,不符合再进右子区间

int search(int id,int l,int r,int ql,int qr,int d){
	if(l==ql&&r==qr){
		if(seg[id].val<d) return -1;
		if(l==r) return l;
		int mid=(l+r)>>1;
		if(seg[id*2].val>=d) return search(id*2,l,mid,ql,mid,d);
		else return search(id*2+1,mid+1,r,mid+1,qr,d);
	}
	int mid=(l+r)>>1;
	if(qr<=mid) return search(id*2,l,mid,ql,qr,d);
	else if(ql>mid)return search(id*2+1,mid+1,r,ql,qr,d);
	else{
		int pos=search(id*2,l,mid,ql,mid,d);
		if(pos==-1) return search(id*2+1,mid+1,r,mid+1,qr,d);
		else return pos;
	}
}

C o d e : Code: Code:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n';
typedef long long ll;
typedef unsigned long long u64;
typedef pair<string,int> PII;
const int N=2e5+10,mod=998244353;
template<class T>
void read(T &x){
	x=0; char c; int sign=1;
	do{ c=getchar(); if(c=='-') sign=-1;}while(!isdigit(c));
	do{ x=x*10+c-'0',c=getchar();}while(isdigit(c));
	x*=sign;
}
int n,q,a[N];
struct node{
	int val;
}seg[N*4];
void update(int id){
	seg[id].val=max(seg[id*2].val,seg[id*2+1].val);
}

void build(int id,int l,int r){
	if(l==r) seg[id].val=a[l];
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		update(id);
	}
}
void change(int id,int l,int r,int pos,int valx){
	if(l==r) seg[id].val=valx;
	else{
		int mid=(l+r)>>1;
		if(pos<=mid) change(id*2,l,mid,pos,valx);
		else change(id*2+1,mid+1,r,pos,valx);
		update(id);
	}
}
int search(int id,int l,int r,int ql,int qr,int d){
	if(l==ql&&r==qr){
		if(seg[id].val<d) return -1;
		if(l==r) return l;
		int mid=(l+r)>>1;
		if(seg[id*2].val>=d) return search(id*2,l,mid,ql,mid,d);
		else return search(id*2+1,mid+1,r,mid+1,qr,d);
	}
	int mid=(l+r)>>1;
	if(qr<=mid) return search(id*2,l,mid,ql,qr,d);
	else if(ql>mid)return search(id*2+1,mid+1,r,ql,qr,d);
	else{
		int pos=search(id*2,l,mid,ql,mid,d);
		if(pos==-1) return search(id*2+1,mid+1,r,mid+1,qr,d);
		else return pos;
	}
}
int main(){
	read(n),read(q);
	for(int i=1;i<=n;i++) read(a[i]);
	build(1,1,n);
	while(q--){
		int op;read(op);
		if(op==1){
			int x,d;read(x),read(d);
			change(1,1,n,x,d);
		}else{
			int l,r,d;read(l),read(r),read(d);
			int ans=search(1,1,n,l,r,d);
			printf("%d\n",ans);
		}
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值