第二课:线段树
单点修改+区间查询最值:
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
x∗t1+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
(x∗t1.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.mul∗t2.mul,t1.add∗t2.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 val∗tx.mul+size∗tx.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;
}