线段树模板
线段树是解决区间问题的好方法。通常作为辅助工具来优化算法的时间复杂度。
- 线段树求和
- 区间极值问题
- 区间GCD
线段树求和
- 节点的数据结构
struct SegmentTree {
int leftRange;//线段左端点
int rightRange;//线段右端点
int value;//节点值
} st[maxn<<2];
- 建树
void buildST(int left = 1, int right = N, int node = 1) {
st[node].leftRange = left;
st[node].rightRange = right;//范围初始化
if(left == right){//若左端点等于右端点,则不再需要继续递归
Input(node);
return ;
}
int mid = (left + right) >> 1;
buildST(left , mid , node << 1);//左子树建树
buildST(mid + 1 , right , node << 1 | 1);//右子树建树
maintainST(node);
}
//直接调用buildST();即可
- 单点更新
void Update(int position, int num, int node = 1) {
int left = st[node].leftRange;
int right = st[node].rightRange;
if(left == right){
st[node].value += num;
return;
}
int mid = (left + right) >> 1;
//递归查找子树
if(position <= mid) Update(position , num, node << 1);
else Update(position , num, node << 1 | 1);
//节点更新
maintainST(node);
}
- 查询
int Query(int ll ,int rr , int node = 1) {
int left = st[node].leftRange;
int right = st[node].rightRange;
//如果是单个节点,则不需要继续递归计算
if(ll <= left && right<=rr) return st[node].value;
int mid = (left + right) >> 1;
int ret = 0;
//递归查询,【注意和更新的差别】
if(ll <= mid) ret += Query(ll ,rr, node << 1);
if(rr > mid) ret += Query(ll ,rr, node << 1 | 1);
return ret;
}
- 维护
int maintainST(int node) {
st[node].value = st[node << 1].value + st[node << 1 | 1].value;
}
简单例题 HDU1166 敌兵布阵
一道纯模板题。
#include<cstdio>
const int maxn = 50000;
int N;
struct SegmentTree {
int leftRange;
int rightRange;
int value;
};
SegmentTree st[maxn << 2];
void buildST(int left = 1, int right = N, int node = 1) {
st[node].leftRange = left;
st[node].rightRange = right;
st[node].value = 0;
if(left == right) {
return ;
}
int mid = (left + right) >> 1;
buildST(left, mid, node << 1);
buildST(mid + 1, right, node << 1 | 1);
}
void Update(int position, int num, int node = 1) {
int left = st[node].leftRange;
int right = st[node].rightRange;
if(left == right) {
st[node].value += num;
return;
}
int mid = (left + right) >> 1;
if(position <= mid) Update(position, num, node << 1);
else Update(position, num, node << 1 | 1);
st[node].value = st[node << 1].value + st[node << 1 | 1].value;//节点更新
}
int Query(int ll, int rr, int node = 1) {
int left = st[node].leftRange;
int right = st[node].rightRange;
if(ll <= left && right <= rr) return st[node].value;
int mid = (left + right) >> 1;
int ret = 0;
if(ll <= mid) ret += Query(ll, rr, node << 1);
if(rr > mid) ret += Query(ll, rr, node << 1 | 1);
return ret;
}
int main() {
int T, x, y;
scanf("%d", &T);
for(int CASE = 1; CASE <= T; CASE++) {
printf("Case %d:\n", CASE);
scanf("%d", &N);
buildST();
for(int i = 1; i <= N; i++) {
int x;
scanf("%d", &x);
Update(i, x);
}
char op[8];
while(scanf("%s", op) == 1 && op[0] != 'E') {
scanf("%d%d", &x, &y);
if(op[0] == 'A') {
Update(x, y);
} else if(op[0] == 'S') {
Update(x, -y);
} else {
printf("%d\n", Query(x, y));
}
}
}
}
RMQ问题
只需要将维护函数和查询函数做下修改即可。(以求最大值为例)
- 维护
int maintainST(int node) {
st[node].value = max(st[node << 1].value, st[node << 1 | 1].value);
}
-查询
int Query(int ll, int rr, int node = 1) {
int left = st[node].leftRange;
int right = st[node].rightRange;
if(ll <= left && right <= rr) return st[node].value;
int mid = (left + right) >> 1;
//*******************不同部分***************************
int ret = 0;
if(ll <= mid) ret = max(ret, Query(ll, rr, node << 1));
if(rr > mid) ret = max(ret, Query(ll, rr, node << 1 | 1));
//*****************************************************
return ret;
}
区间GCD问题
- 维护
int maintainST(int node) {
st[node].value = __gcd(st[node << 1].value, st[node << 1 | 1].value);
}
-查询
int Query(int ll, int rr, int node = 1) {
int left = st[node].leftRange;
int right = st[node].rightRange;
if(ll <= left && right <= rr) return st[node].value;
int mid = (left + right) >> 1;
if(rr <= mid)return Query(ll, rr, node << 1);
if(ll > mid)return Query(ll, rr, node << 1 | 1);
return gcd(Query(ll, rr, node << 1), Query(ll, rr, node << 1 | 1));
}