线段树模板

线段树模板-区间求和为例

建树

1
2
3
4
5
6
7
8
9
10
void build(int x,int l,int r) {
if(l==r) {
t[x]=a[l];//将线段上的值转换到树上 根据节点属性的设置来设置此处内容
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(x);//向上传递状态
}

区间修改

1
2
3
4
5
6
7
8
9
10
11
12
void update(int x,int l,int r,int L,int R,int v) {
if(l>=L&&r<=R) {
lazy[x]+=v;//懒惰标记 *只打标记 不向下传递*
t[x]+=v*(r-l+1);//区间和操作 *只进行此次操作*
return;
}
pushdown(x,r-l+1);//向下传递 并清空自身节点 *计算子节点标记贡献*
int mid=(l+r)>>1;
if(L<=mid)update(ls,l,mid,L,R,v);
if(mid<R) update(rs,mid+1,r,L,R,v);
pushup(x);//向上更新状态
}

区间查询

1
2
3
4
5
6
7
8
9
10
11
int query(int x,int l,int r,int L,int R){
if(l>=L&&r<=R) {
return t[x];
}
pushdown(x);//向下传递 并清空自身节点 *计算子节点标记贡献*
int mid=(l+r)>>1;
int ans=0;
if(L<=mid)ans+=query(ls,l,mid,L,R);//计算子节点和
if(mid<R)ans+=query(rs,mid+1,r,L,R);
return ans;
}

pushdown函数

1
2
3
4
5
6
7
8
9
void pushdown(int x,int len) {
if(lazy[x]) {
lazy[ls]+=lazy[x];//向下传递标记
lazy[rs]+=lazy[x];
tr[ls]+=lazy[x]*len/2;//*计算子节点标记贡献*
tr[rs]+=lazy[x]*len/2;
lazy[x]=0;//清空自身节点
}
}

例题

更新一个例题,因为原来的模板有些单薄了。

题目描述

看例题吧,主要就是三个操作,区间加c,区间乘c,区间求和。要注意的就是加和乘的顺序问题,有两种操作,就需要两个标记数组。具体来说,就是在标记向下传递时,加法的标记要收到乘法的影响,越深的标记代表时间越早,则要收到新的标记的影响。

比较重要的pushdown函数单独列出来吧

pushdown函数

1
2
3
4
5
6
7
8
9
10
void pushdown(int x,int l,int r){
t[ls]=(t[ls]*mu[x]+sum[x]*(mid-l+1))%mod;
t[rs]=(t[rs]*mu[x]+sum[x]*(r-mid))%mod;
mu[ls]=(mu[ls]*mu[x])%mod;
mu[rs]=(mu[rs]*mu[x])%mod;
sum[ls]=(sum[ls]*mu[x]+sum[x])%mod;
sum[rs]=(sum[rs]*mu[x]+sum[x])%mod;
mu[x]=1;
sum[x]=0;
}

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=1e5+10;
int a[N],t[N<<2],mu[N<<2],sum[N<<2];
#define ls 2*x
#define rs 2*x+1
#define mid ((l+r)/2)
int n,m,mod;
void pushup(int x){
t[x]=(t[ls]+t[rs])%mod;
}
void pushdown(int x,int l,int r){
t[ls]=(t[ls]*mu[x]+sum[x]*(mid-l+1))%mod;
t[rs]=(t[rs]*mu[x]+sum[x]*(r-mid))%mod;
mu[ls]=(mu[ls]*mu[x])%mod;
mu[rs]=(mu[rs]*mu[x])%mod;
sum[ls]=(sum[ls]*mu[x]+sum[x])%mod;
sum[rs]=(sum[rs]*mu[x]+sum[x])%mod;
mu[x]=1;
sum[x]=0;
}
void build(int x,int l,int r){
mu[x]=1;
sum[x]=0;
if(l==r){
t[x]=a[l];
return;
}
build(ls,l,mid);
build(rs,mid+1,r);
pushup(x);
}
void mult(int x,int l,int r,int L,int R,int c){
if(l>=L&&r<=R){
mu[x]=(mu[x]*c)%mod;
sum[x]=(sum[x]*c)%mod;
t[x]=(t[x]*c)%mod;
return;
}
pushdown(x,l,r);
if(L<=mid)mult(ls,l,mid,L,R,c);
if(mid<R)mult(rs,mid+1,r,L,R,c);
pushup(x);
}
void add(int x,int l,int r,int L,int R,int c){
if(l>=L&&r<=R){
sum[x]=(sum[x]+c)%mod;
t[x]=(t[x]+c*(r-l+1))%mod;
return ;
}
pushdown(x,l,r);
if(L<=mid)add(ls,l,mid,L,R,c);
if(mid<R)add(rs,mid+1,r,L,R,c);
pushup(x);
}
int query(int x,int l,int r,int L,int R){
if(l>=L&&r<=R){
return t[x];
}
pushdown(x,l,r);
int ret=0;
if(L<=mid)ret+=query(ls,l,mid,L,R);
if(mid<R)ret+=query(rs,mid+1,r,L,R);
return ret%mod;
}
signed main(){
IOS
cin>>n>>m>>mod;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
//cout<<query(1,1,n,1,n);
while(m--){
int op,l,r,c;
cin>>op>>l>>r;
if(op==1){
cin>>c;
mult(1,1,n,l,r,c);
}else if(op==2){
cin>>c;
add(1,1,n,l,r,c);
}else {
cout<<query(1,1,n,l,r)<<'\n';
}
}
return 0;
}

再次更新

暑假学到了一种结构体的写法,把线段树的节点更紧密地结合,比以前的写法更好,记录一下。

例题DOS Card

定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct node {
long long mx,mn,mx2,mn2,pir,pir2,pp,pd;//记录节点
};
struct Segmettree {
node t[N<<2];
#define ls 2*x
#define rs 2*x+1
node merge(node x,node y,int l,int r);
void pushup(int x,int l,int r){
t[x]=merge(t[ls],t[rs],l,r);//合并
}
void build(int x,int l,int r);
node query(int x,int l,int r,int L,int R);
}tree;

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);


#define pii pair<int,int>
#define fi first
#define se second

const int N=5e5+10;
const int mod=1e9+7;
int n,m;
int a[N];
struct node {
long long mx,mn,mx2,mn2,pir,pir2,pp,pd;
};
struct Segmettree {
node t[N<<2];
#define ls 2*x
#define rs 2*x+1
node merge(node x,node y,int l,int r){
int mid=(l+r)>>1;
node z;
if(x.mx>y.mx){
z.mx=x.mx;
z.mx2=max(x.mx2,y.mx);
}else{
z.mx=y.mx;
z.mx2=max(y.mx2,x.mx);
}
if(x.mn<y.mn){
z.mn=x.mn;
z.mn2=min(x.mn2,y.mn);
}else{
z.mn=y.mn;
z.mn2=min(y.mn2,x.mn);
}

z.pir2=max({x.pir2,y.pir2,x.pir+y.pir,x.mx+x.mx2-y.mn-y.mn2,x.pp-y.mn,x.mx+y.pd});
z.pir=max({x.pir,y.pir,x.mx-y.mn});

z.pp=max({x.pp,y.pp,x.pir+y.mx,x.mx+y.pir});
if(r-mid>=2)z.pp=max(z.pp,x.mx-y.mn+y.mx);
if(mid-l+1>=2)z.pp=max(z.pp,x.mx+x.mx2-y.mn);

z.pd=max({x.pd,y.pd,x.pir-y.mn,-x.mn+y.pir});
if(r-mid>=2)z.pd=max(z.pd,x.mx-y.mn-y.mn2);
if(mid-l+1>=2)z.pd=max(z.pd,x.mx-x.mn-y.mn);

return z;
}
void pushup(int x,int l,int r){
t[x]=merge(t[ls],t[rs],l,r);
}
void build(int x,int l,int r){
if(l==r){
t[x].mn=t[x].mx=1ll*a[l]*a[l];
t[x].mx2=-1e17;
t[x].mn2=1e17;
t[x].pir=t[x].pir2=t[x].pp=t[x].pd=-1e17;
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(x,l,r);
}
node query(int x,int l,int r,int L,int R){
if(l>=L&&r<=R){
return t[x];
}
int mid=(l+r)>>1;
if(R<=mid)return query(ls,l,mid,L,R);
else if(L>mid)return query(rs,mid+1,r,L,R);
else return merge(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R),l,r);
}
}tree;


signed main() {
int T;
cin>>T;
while(T--) {

cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
tree.build(1,1,n);
while(m--) {
int l,r;
cin>>l>>r;

cout<<tree.query(1,1,n,l,r).pir2<<endl;
}
}
return 0;
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!