线段树专题
清单
· hdu1166 敌兵布阵
· hdu1754 I Heat It
· hdu1698 Just a Hook
· poj3468 A Simple Problem with Integers
· codeforces156A]
· 总结
写在前面
本文作为个人练习使用,不能保证思路每个人都看懂。但是可以提供由易到难的题目清单。
习题
1. hdu1166 敌兵布阵
//区间查询,点修改 (不含懒标记)
#include<iostream>
#include<cstdio>
#include<cstring>
#define lson tree[rt].l,mid,rt<<1
#define rson mid+1,tree[rt].r,rt<<1|1
using namespace std;
typedef long long ll;
const int maxn = 5e4+10;
int ans;
int a[maxn];
struct{
int l,r,sum;
}tree[maxn<<2];
void build(int L,int R,int rt){
tree[rt].l = L,tree[rt].r = R;
if(tree[rt].l == tree[rt].r){
// tree[rt].sum = a[L];
scanf("%d",&tree[rt].sum);
return ;
}
int mid = (L + R)>>1;
build(lson);
build(rson);
tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum;
}
void add(int pos,int val,int rt){
if(tree[rt].l == tree[rt].r){
tree[rt].sum += val;
return ;
}
int l = tree[rt].l, r = tree[rt].r;
int mid = (l+r)>>1;
tree[rt].sum += val;
if(pos<=mid) add(pos,val,rt<<1);
if(pos>mid) add(pos,val,rt<<1|1);
}
void query(int L,int R,int rt){
int l = tree[rt].l, r = tree[rt].r;
int mid = (l + r)>>1;
if(L <=l && r <= R){
ans += tree[rt].sum;
// cout<<l<<" l r "<<r<<" tree[rt].sum "<<tree[rt].sum<<endl;
return ;
}
if(L <= mid) query(L,R,rt<<1);
if(R > mid) query(L,R,rt<<1|1);
}
int main(){
int t; scanf("%d",&t);
for(int p=1;p<=t;p++){
printf("Case %d:\n",p);
int n; scanf("%d",&n);
// for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,n,1);
char op[10];int L,R,pos,val;
while(1){
scanf("%s",op);
if(op[0] == 'E')
break;
if(op[0] == 'A'){
scanf("%d%d",&pos,&val);
add(pos,val,1);
}
if(op[0] == 'S'){
scanf("%d%d",&pos,&val);
add(pos,-val,1);
}
if(op[0] == 'Q'){
ans = 0;
scanf("%d%d",&L,&R);
query(L,R,1);
printf("%d\n",ans);
}
}
}
return 0;
}
/*
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End
Case 1:
6
33
59
*/
2. hdu1754 I Heat It
//单点更新 查询区间最值 (不含懒标记)
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
struct{
int l, r,maxx;
}tree[maxn<<2];
int a[maxn];
void build(int l,int r,int rt){
tree[rt].l = l; tree[rt].r = r;
if(l == r){
tree[rt].maxx = a[l];
return ;
}
int mid = (l + r)>>1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
tree[rt].maxx = max(tree[rt<<1].maxx,tree[rt<<1|1].maxx);
}
int query(int l,int r,int rt){
if(tree[rt].l >= l && tree[rt].r <= r)
return tree[rt].maxx;
int mid = (tree[rt].l + tree[rt].r)>>1; //误写成mid = (l+r)>>1
if(r <= mid){
return query(l,r,rt<<1);
}
if(l > mid){
return query(l,r,rt<<1|1);
}
if(r > mid&&l<=mid){
return max(query(l,mid,rt<<1),query(mid+1,r,rt<<1|1));
}
}
//int query(int l,int r,int rt){
// if(l <= tree[rt].l && tree[rt].r <= r){
// return tree[rt].maxx;
// }
// int mid = (tree[rt].l + tree[rt].r)>>1;
// int MAX = -1;
// if(l <= mid) //fuck(r<=mid)
// MAX = max(MAX,query(l,r,rt<<1));
// if(r > mid)
// MAX = max(MAX,query(l,r,rt<<1|1));
// return MAX;
//}
void update(int pos,int k,int rt){
if(tree[rt].l == tree[rt].r){
tree[rt].maxx = k;
return ;
}
int mid = (tree[rt].l + tree[rt].r)>>1;
if(pos <= mid){
update(pos,k,rt<<1); //误写成update(tree[rt].l,mid,rt<<1);
}
else{
update(pos,k,rt<<1|1);
}
tree[rt].maxx = max(tree[rt<<1].maxx,tree[rt<<1|1].maxx);
}
int n,m;
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,n,1);
while(m--){
char op[5];int l,r;
scanf("%s%d%d",op,&l,&r);
if(op[0] == 'U'){
update(l,r,1);
}
if(op[0] == 'Q'){
printf("%d\n",query(l,r,1));
}
// for(int i= 1 ;i<=4*n;i++){
// cout<<tree[i].maxx<<' ';
// }
// cout<<endl;
}
}
return 0;
}
/*
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
5
6
5
9
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct{
int l,r,val,lazy;
}tree[maxn<<2];
int getlen(int rt){
return tree[rt].r - tree[rt].l + 1;
}
void push_down(int rt){
if(tree[rt].lazy){
tree[rt<<1].lazy = tree[rt].lazy;
tree[rt<<1|1].lazy = tree[rt].lazy;
tree[rt<<1].val = getlen(rt<<1)*tree[rt].lazy;
tree[rt<<1|1].val = getlen(rt<<1|1)*tree[rt].lazy;
tree[rt].lazy = 0;
}
}
void build(int l,int r,int rt){
tree[rt].l = l, tree[rt].r = r,tree[rt].lazy = 0;
int mid = (l + r)>>1;
if(l == r){
tree[rt].val = 1;
return ;
}
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
tree[rt].val = tree[rt<<1].val + tree[rt<<1|1].val;
}
void update(int L,int R,int num,int rt){
int l = tree[rt].l, r = tree[rt].r;
if(L <= l && r <= R){
tree[rt].lazy = num;
tree[rt].val = getlen(rt)*num;
return ;
}
push_down(rt);
int mid = (l + r)>>1;
if(L<=mid) update(L,R,num,rt<<1);
if(R>mid) update(L,R,num,rt<<1|1);
tree[rt].val = tree[rt<<1].val + tree[rt<<1|1].val;
}
int query(){
return tree[1].val;
}
int main(){
int ma=maxn<<2+1;
cout<<ma<<endl;
int t;int cnt = 0;
scanf("%d",&t);
while(t--){
cnt++;
int n,m; scanf("%d%d",&n,&m);
// for(int i=1;i<=(n<<2+2);i++) tree[i].lazy = 0;
build(1,n,1);
int L,R,val;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&L,&R,&val);
update(L,R,val,1);
}
printf("Case %d: The total value of the hook is %d.\n",cnt,query());
}
return 0;
}
/*
4
10
2
1 5 2
5 9 3
10
2
2 5 2
5 9 2
10
2
1 3 3
5 5 3
10
2
1 8 3
5 8 2
Case 1: The total value of the hook is 24.
*/
4. poj3468 A Simple Problem with Integers
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct{
ll l,r;
ll lazy;
ll sum;
}tree[(maxn<<2)+10];
ll getlen(int rt){
return tree[rt].r - tree[rt].l + 1;
}
ll ans;
void push_down(int rt){
if(tree[rt].lazy){
tree[rt<<1].lazy += tree[rt].lazy;
tree[rt<<1|1].lazy += tree[rt].lazy;
tree[rt<<1].sum += tree[rt].lazy*getlen(rt<<1);
tree[rt<<1|1].sum += tree[rt].lazy*getlen(rt<<1|1);
tree[rt].lazy = 0;
}
}
void build(ll l,ll r,int rt){
tree[rt].l = l, tree[rt].r = r,tree[rt].lazy=0;
ll mid = (l + r)>>1;
tree[rt].sum=0;
if(l == r){
scanf("%lld",&tree[rt].sum);
return ;
}
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum;
}
void update(ll L,ll R,ll charge,int rt){
ll l = tree[rt].l, r = tree[rt].r;
ll mid = (l + r)>>1;
if(L <= l && r <= R){
tree[rt].lazy += charge;
tree[rt].sum += getlen(rt)*charge;
return ;
}
push_down(rt);
if(L <= mid) update(L,R,charge,rt<<1);
if(mid < R) update(L,R,charge,rt<<1|1);
tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum;
}
void query(ll L,ll R,int rt){
ll l = tree[rt].l, r = tree[rt].r;
ll mid = (l + r)>>1;
if(L <= l && r <= R){
ans += tree[rt].sum;
return ;
}
push_down(rt);
if(L <= mid) query(L,R,rt<<1);
if(mid < R) query(L,R,rt<<1|1);
tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum;
}
int main(){
ll n,m;
while(~scanf("%lld%lld",&n,&m)){
build(1,n,1);
// for(ll i=1;i<=(n<<2)+2;i++) tree[i].lazy = 0;
char op[10];
while(m--){
scanf("%s",op);ll L,R,charge;
if(op[0] == 'Q'){
scanf("%lld%lld",&L,&R);
ans = 0;
query(L,R,1);
printf("%lld\n",ans);
} else{
scanf("%lld%lld%lld",&L,&R,&charge);
update(L,R,charge,1);
}
}
}
return 0;
}
/*
10 11
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
C 2 4 4
Q 1 3
C 5 9 6
Q 4 7
C 1 1 2
Q 1 1
4
55
9
15
17
53
3
*/
5. codeforces356A
- 可以用并查集
- 可以用set
- 可以用线段树
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 3e5+10;
int a[maxn];
struct{
int l, r, endd;
}tree[maxn<<2];
void build(int l,int r,int rt){
tree[rt].l = l, tree[rt].r = r;
if(l == r) return ;
int mid = (l + r)>>1;
build(l , mid , rt<<1);
build(mid + 1, r , rt<<1|1);
}
void update(int L,int R,int num,int rt){
int l = tree[rt].l, r = tree[rt].r;
int mid = (l + r)>>1;
if(tree[rt].endd){
if(a[tree[rt].endd] == 0 && num != tree[rt].endd) a[tree[rt].endd] = num;
return ;
}
if(L <= l && r <= R){
tree[rt].endd = num;
for(int i=l;i<=r;i++){
if(i != num && a[i] == 0) a[i] = num;
}
return ;
}
if(L <= mid) update(L,R,num,rt<<1);
if(mid < R) update(L,R,num,rt<<1|1);
}
int main(){
int n,m; scanf("%d%d",&n,&m);
build(1,n,1);
int L,R,val;
while(m--){
scanf("%d%d%d",&L,&R,&val);
update(L,R,val,1);
}
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
6. 最大连续子段和
题意
点修改,求区间最大连续子段和。
注意
- 这里的query的push_up非常巧妙。返回结构体,而且不是正常的子树向父节点的push_up。
- 我们的TREE里有几个参数是,一点一点发现需要用到的,比如我们发现要求lmax和rmax,接着发现还需要求sum。
/*
* Author: Chen_zhuozhuo
* Created Time: 2020/3/18 11:35:20
* File Name: a.cpp
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <time.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxint = -1u>>1;
const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 10;
int n,m,a[maxn],op,L,R,pos,val;
int Max(int a, int b, int c){
return max(a, max(b, c));
}
struct TREE{
int l, r, sum, lmax, rmax, max;
}tree[maxn<<2];
void push_up(int rt){
tree[rt].max= Max(tree[rt<<1].max, tree[rt<<1|1].max, tree[rt<<1].rmax + tree[rt<<1|1].lmax);
tree[rt].lmax = max(tree[rt<<1].lmax, tree[rt<<1].sum + tree[rt<<1|1].lmax), tree[rt].rmax = max(tree[rt<<1|1].rmax, tree[rt<<1|1].sum + tree[rt<<1].rmax);
tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum;
}
void build(int L, int R, int rt){
tree[rt].l = L, tree[rt].r = R;
if(L == R){
tree[rt].sum = tree[rt].lmax = tree[rt].rmax = tree[rt].max = a[L];
return ;
}
int mid = L + R >> 1;
build(L, mid, rt<<1);
build(mid + 1, R, rt<<1|1);
push_up(rt);
}
void update(int pos, int val, int rt){
int l = tree[rt].l, r = tree[rt].r;
if(l == r){
tree[rt].max = tree[rt].sum = tree[rt].lmax = tree[rt].rmax = val;
return ;
}
int mid = l + r >> 1;
if(pos <= mid){
update(pos, val, rt<<1);
}
if(pos > mid){
update(pos, val, rt<<1|1);
}
push_up(rt);
}
TREE query(int L, int R, int rt){
int l = tree[rt].l, r = tree[rt].r;
if(L <= l && r <= R)
return tree[rt];
int mid = l + r >> 1;
if(R <= mid)
return query(L, R, rt<<1);
else if(L > mid)
return query(L, R, rt<<1|1);
else{
TREE res, left, right;
left = query(L, R, rt<<1);
right = query(L, R, rt<<1|1);
res.sum = left.sum + right.sum;
res.max = Max(left.max, right.max, left.rmax + right.lmax);
res.lmax = max(left.lmax, left.sum + right.lmax);
res.rmax = max(right.rmax, right.sum + left.rmax);
return res;
}
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++){
cin>>a[i];
}
build(1, n, 1);
for(int i=1; i<=m; i++){
cin>>op;
if(op == 1){
cin>>L>>R;
if(L > R)
swap(L, R);
cout<<query(L, R, 1).max<<endl;
}
else{
cin>>pos>>val;
update(pos, val, 1);
}
}
return 0;
}
/*
输入样例:
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2
输出样例:
2
-1
*/
总结
- push_down在 if (L <= l && r <= R)之后,否则在叶子节点接着向下访问,会导致数组越界RE。
- 在update中,先下传懒标记在push_up.
- 懒标记是给儿子用的. 儿子用父亲的lazy_tag。
例.tree[rt<<1].sum += tree[rt].lazy * getlen(rt<<1); - push_up操作一共有3次
(1)Query() 对于大多数运算,应该是可以不加push_up的.因为原本tree[rt].sum=getlen(rt) * lazy;加上push_up后tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum即tree[rt].lazygetlen(rt<<1)+tree[rt].lazygetlen(rt<<1|1);
提取公因式后,得到tree[rt].lazy*(getlen(rt<<1)+getlen(rt<<1|1)),即tree[rt].lazy*getlen(rt);两者应当是相等的。
但是如果本题不是加法,而是不满足结合律的运算。最好加上push_up。
(2)Update()
(3)Build() - push_down操作一共有两次
(1)Update()
(2)Query() - tree[maxn<<2+100] 加运算优先,变成了左移102位,int(32位)左移102是0.(居然报tle)
- 最容易出错的地方
(1)+= 写成 =
(2)如图所示
(3)如图所示
- push_donw什么时候用?破坏区间一致性的时候。