线段树将区间分为若干个子区间,子区间又继续分,直到区间为一个点(l==r) 对于父区间 [l,r] ,其子区间为 [l,(l+r)/2][(l+r)/2+1,r] 线段树一般用于求区间的值,如区间最值,区间求和等。 代码实现中, 假设节点下标从1开始,若某节点下标为x,那么其左子区间下标为2x,右子区间下标为2x+1,父节点下标为x/2;

在这里插入图片描述

常用宏定义

1
2
3
#define Mid ((l+r)>>1)          //中间值   注意括号
#define lson rt<<1,l,Mid //左结点
#define rson rt<<11,Mid+1,r //右结点

建树

建树从根节点开始,递归建立左右子树,直到叶子节点,然后反向赋值,父节点的值=F(左节点的值,右节点的值),这个F根据题意确定,若求区间最大值则为max,若求区间和则为sum。

1
2
3
4
5
6
7
8
9
10
void build(int rt,int l,int r)      //建编号为rt的区间为[l,r]的树,主函数传进来的固定是(1,1,n)
{
if(l==r){ //叶子结点赋初值,注意下标,Max的是编号,val原数组的是l,看图可以理解
Max[rt] = val[l];
}else{ //建左右子树
build(lson);
build(rson);
Max[rt] = max( Max[rt<<1], Max[rt<<11]); //父结点Max值为Max(左子结点,右子结点)
}
}

查询

查询为区间查询(只是查询某个点的话不需要线段树),即在区间里查询某个特性值,每次查询都是从跟结点开始往下,根据查询区间和当前区间的区间位置判断是要去左右子区间查询,还是直接返回。如果被查询区间是查询区间的子区间则直接返回子区间的值,如在[1,6]里查询[1,12]就返回[1,6]的值,不再往下查询。

1
2
3
4
5
6
7
8
9
10
11
12
void query(int rt,int l,int r,int L,int R)  //在[l,r]里查询[L,R]的值,[L,R]一直不变,[l,r]变
{
if(L <= l && r <= R){
ans1 = max(ans1,Max[rt]);
ans2 = min(ans2,Min[rt]);
}else{
if( L <= Mid) //查询区间在当前区间的左半区间有内容,如在[1,6]里查询[2,3]
query(lson,L,R);
if( R > Mid) //同理去右子区间,注意不能有else,因为有横跨左右的情况,如[1,6]里查询[2,5]
query(rson,L,R);
}
}

单点更新

1
2
3
4
5
6
7
8
9
10
11
12
void update(int rt,int l,int r,int pos,int num)
{
if(l == r && r == pos){ //到对应的叶结点
Max[rt] = num;
}else{
if( pos <= Mid)
update(lson,pos,num);
if( pos > Mid) //或者直接else,点不可能同时在两个区间里
update(rson,pos,num);
Max[rt] = max( Max[rt<<1], Max[rt<<11]);
}
}

区间更新

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

void pushDown(int rt,int len)
{
add[rt<<1] = add[rt<<11] = add[rt];
sum[rt<<1] = (len-(len>>1))*add[rt];
sum[rt<<11] = (len>>1)*add[rt];
add[rt] = 0;
}

void update(int rt,int l,int r,int L,int R,int z)
{
if(L <= l && r <= R){
add[rt] = z;//或 add[rt]+=z; 根据题意而定
sum[rt] = (r-l+1)*add[rt];
}else{
if(add[rt])
pushDown(rt,r-l+1);
if(L <= Mid)
update(lson,L,R,z);
if(R > Mid)
update(rson,L,R,z);
sum[rt] = sum[rt<<1] + sum[rt<<11];
}
}

例题

HDU 1166 敌兵布阵

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstdio>
#include <map>

using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define PI acos(-1)

typedef long long ll;
int dir8[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int dir4[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
const int INF = 0x3f3f3f3fll;
const long long intF = 0x3f3f3f3f3f3f3f3fll;
const int MAXn = 1e5 + 5;
const int mod = 1e9 + 7;

struct node
{
int l, r, w, flag;
int mid() { return (r + l) / 2; }
int dis() { return r - l + 1; }
} a[MAXn * 4];

void build(int k, int l, int r)
{
a[k].l=l;
a[k].r=r;
a[k].w=0;
a[k].flag=0;
if (l == r)
{
cin >> a[k].w;
return;
}
build(k << 1, l ,a[k].mid());
build(k<<11, a[k].mid()+1, r );
a[k].w=a[k<<1].w+a[k<<11].w;
}

void down(int k)
{
a[k << 1].w += a[k].flag * a[k << 1].dis();
a[k << 1 1].w += a[k].flag * a[k << 1 1].dis();
a[k << 1].flag += a[k].flag;
a[k << 1 1].flag += a[k].flag;
a[k].flag = 0;
}

void update(int k, int l, int r, int w)
{
if(a[k].l==l&&a[k].r==a[k].l)
{
a[k].w+=a[k].dis()*w;
a[k].flag+=w;
return ;
}
if(a[k].flag)
down(k);
if(a[k].mid()>=l)
update(k<<1,l,r,w);
if(a[k].mid()<r)
update(k<<11,l,r,w);
a[k].w=a[k<<1].w+a[k<<11].w;
}

int query(int k, int l, int r)
{
if (a[k].l >= l && a[k].r <= r)
return a[k].w;
if (a[k].flag)
down(k);
int sum = 0;
if (a[k].mid() >= l)
sum += query(k << 1, l, r);
if (a[k].mid() < r)
sum += query(k << 1 1, l, r);
a[k].w = a[k << 1].w + a[k << 1 1].w;
return sum;
}


int main()
{
//IN;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

int T,ii=0;
cin >> T;
while (T--)
{
ii++;
cout<<"Case "<<ii<<":"<<endl;
int n;
cin>>n;
build(1,1,n);
while(1)
{
string s;
int i,j;
cin>>s;
if(s=="End")
break;
cin>>i>>j;
if(s=="Sub")
j=-j;
if(s=="Query"){
cout<<query(1,i,j)<<endl;
continue;
}
update(1,i,i,j);
}
}
return 0;
}

评论




博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

载入天数...载入时分秒... 本站使用 Volantis 作为主题 鲁ICP备-20012065号