线段树

[线段树]
本质为二叉树
用来区间查询,区间修改,
单点查询,单点修改
运用结构体存储。
struct node{
	int sum,laze;
}tree[N*4];//四倍空间 
//建树
void build_tree(int id,int l,int r){ 
	if(l==r){
		tree[id].sum=a[l];
		return;
	}
	int mid=(l+r)/2;
	build_tree(id*2,l,mid);
	build_tree(id*2+1,mid+1,r);
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
//区间和查询 
int query(int id,int l,int r,int fl,int fr){
	if(l==fl&&r==fr){
		return tree[id].sum;
	}
	int mid=(l+r)/2;
	if(fr<=mid){
		return query(id*2,l,mid,fl,fr);
	}else if(fl>mid){
		return query(id*2+1,mid+1,r,fl,fr);
	}else{
		return query(id*2,l,mid,fl,mid)
			+query(id*2+1,mid+1,r,mid+1,fr);
	}
}
//单点修改 
void change_one(int id,int l,int r,int fn,int cs){
	if(l==r){
		tree[id].sum+=cs;
		return ;
	}
	int mid=(l+r)/2;
	if(fn<=mid)change_one(id*2,l,mid,fn,cs);
	else change_one(id*2+1,mid+1,r,fn,cs);
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
//push三件套
void pushup(int id){
	tree[id].sum=tree[id*2].sum+t[id*2+1].sum;
}
void pushdown(int id,int l,int r){
	if(tree[id].laze){
		int mid=(l+r)/2;
		put_tag(id*2,l,mid,tree[id].laze);
		put_tag(id*2+1,mid+1,r,tree[id].laze);
		tree[id].laze=0;
	}
}
void put_tag(int id,int l,int r,int val){
	tree[id].sum+=1ll*(r-l+1)*val;
	tree[id].laze+=val;
}
//区间查询(laze版) 
long long query(int id,int l,int r,int fl,int fr){
	if(fl==l&&fr==r){
		return tree[id].sum;
	}
	pushdown(id,l,r);
	int mid=(l+r)/2;
	if(fr<=mid){
		return query(id*2,l,mid,fl,fr);
	}else if(fl>mid){
		return query(id*2+1,mid+1,r,fl,fr);
	}else{
		return query(id*2,l,mid,fl,mid)
		+query(id*2+1,mid+1,r,mid+1,fr);
	}
}
//区间加(laze) 
void change(int id,int l,int r,int fl,int fr,long long val){
	if(fl==l&&fr==r){
		put_tag(id,l,r,val);
		return ;
	}
	int mid=(l+r)/2;
	if(fr<=mid){
		change(id*2,l,mid,fl,fr,val);
	}else if(fl>mid){
		change(id*2+1,mid+1,r,fl,fr,val);
	}else{
		change(id*2,l,mid,fl,mid,val);
		change(id*2+1,mid+1,r,mid+1,fr,val);
	}
	pushup(id);
}
1.
accoders线段树 区间最小
思路:
区间查询最小值,把return query(id*2,l,mid,fl,mid)+query(id*2+1,mid+1,r,mid+1,fr);
改为min(query(id*2,l,mid,fl,mid),query(id*2+1,mid+1,r,mid+1,fr));
即可 。
code: 
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
struct node{
    int sum,laze;
}tree[N*4];
void build(int id,int l,int r){
    if(l==r){
        tree[id].sum=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
}
int query(int id,int l,int r,int fl,int fr){
    if(fl==l&&fr==r){
        return tree[id].sum;
    }
    int mid=(l+r)/2;
    if(fr<=mid){
        return query(id*2,l,mid,fl,fr);
    }else if(fl>mid){
        return query(id*2+1,mid+1,r,fl,fr);
    }else{
        return min(query(id*2,l,mid,fl,mid),query(id*2+1,mid+1,r,mid+1,fr));
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,1,n);     
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        cout<<query(1,1,n,x,y)<<" ";
    }
    return 0;
}
2.
accoders线段树 带修改的区间最小
思路:
加上单点修改即可,将tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
改为 tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
即可。 
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
struct node{
    int sum,laze;
}tree[N*4];
void build(int id,int l,int r){
    if(l==r){
        tree[id].sum=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
    tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
}
int query(int id,int l,int r,int fl,int fr){
    if(fl==l&&fr==r){
        return tree[id].sum;
    }
    int mid=(l+r)/2;
    if(fr<=mid){
        return query(id*2,l,mid,fl,fr);
    }else if(fl>mid){
        return query(id*2+1,mid+1,r,fl,fr);
    }else{
        return min(query(id*2,l,mid,fl,mid),query(id*2+1,mid+1,r,mid+1,fr));
    }
}
void change(int id,int l,int r,int fn,int val){
    if(l==r){
        tree[id].sum=val;
        return ;
    }
    int mid=(l+r)/2;
    if(fn<=mid){
        change(id*2,l,mid,fn,val);
    }else{
        change(id*2+1,mid+1,r,fn,val);
    }
    tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    build(1,1,n);     
    for(int i=1;i<=m;i++){
        int f;
        cin>>f;
        if(f==1){
            int x,y;
            cin>>x>>y;
            cout<<query(1,1,n,x,y)<<" ";
        }else{
            int x,y;
            cin>>x>>y;
            change(1,1,n,x,y);
        }
    }
    return 0;
}
3.
luogu P3374 【模板】树状数组1
什么,你问为什么树状数组的题要用线段树写?
(别问(划掉))
因为他的区间查询正好是线段树能解决的,也是线段树的弱化版,适合练手。
思路:
考察区间加和区间和,套板子即可。
注:
要开long long。 
code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int a[N]; 
struct node{
	long long sum,laze;
}tree[N*4];
void build_tree(int id,int l,int r){
	if(l==r){
		tree[id].sum=a[l];
		return;
	}
	int mid=(l+r)/2;
	build_tree(id*2,l,mid);
	build_tree(id*2+1,mid+1,r);
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
int query(int id,int l,int r,int fl,int fr){
	if(l==fl&&r==fr){
		return tree[id].sum;
	}
	int mid=(l+r)/2;
	if(fr<=mid){
		return query(id*2,l,mid,fl,fr);
	}else if(fl>mid){
		return query(id*2+1,mid+1,r,fl,fr);
	}else{
		return query(id*2,l,mid,fl,mid)
			+query(id*2+1,mid+1,r,mid+1,fr);
	}
}
void change_one(int id,int l,int r,int fn,int cs){
	if(l==r){
		tree[id].sum+=cs;
		return ;
	}
	int mid=(l+r)/2;
	if(fn<=mid)change_one(id*2,l,mid,fn,cs);
	else change_one(id*2+1,mid+1,r,fn,cs);
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build_tree(1,1,n);
	for(int i=1;i<=m;i++){
		int f,x,y;
		cin>>f>>x>>y;
		if(f==1){
			change_one(1,1,n,x,y);
		}else{
			cout<<query(1,1,n,x,y)<<"\n";
		}
	}
	return 0;
}
4.
luogu P3368 【模板】树状数组 2
来都来了,直接(水(划掉))做完好了。
思路:
区间加和单点查,要用到laze了。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int a[N];
struct node{
	int sum,laze;
}tree[N*4];
void pushup(int id){
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
void put_tag(int id,int l,int r,int val){
	tree[id].sum+=1ll*(r-l+1)*val;
	tree[id].laze+=val;
}
void pushdown(int id,int l,int r){
	if(tree[id].laze){
		int mid=(l+r)/2;
		put_tag(id*2,l,mid,tree[id].laze);
		put_tag(id*2+1,mid+1,r,tree[id].laze);
		tree[id].laze=0;
	}
}
void build(int id,int l,int r){
	if(l==r){
		tree[id].sum=a[l];
		return ;
	}
	int mid=(l+r)/2;
	build(id*2,l,mid);
	build(id*2+1,mid+1,r);
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
long long query(int id,int l,int r,int fl,int fr){
	if(fl==l&&fr==r){
		return tree[id].sum;
	}
	pushdown(id,l,r);
	int mid=(l+r)/2;
	if(fr<=mid){
		return query(id*2,l,mid,fl,fr);
	}else if(fl>mid){
		return query(id*2+1,mid+1,r,fl,fr);
	}else{
		return query(id*2,l,mid,fl,mid)
		+query(id*2+1,mid+1,r,mid+1,fr);
	}
}
void change(int id,int l,int r,int fl,int fr,long long val){
	if(fl==l&&fr==r){
		put_tag(id,l,r,val);
		return ;
	}
	int mid=(l+r)/2;
	if(fr<=mid){
		change(id*2,l,mid,fl,fr,val);
	}else if(fl>mid){
		change(id*2+1,mid+1,r,fl,fr,val);
	}else{
		change(id*2,l,mid,fl,mid,val);
		change(id*2+1,mid+1,r,mid+1,fr,val);
	}
	pushup(id);
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int f;
		cin>>f;
		if(f==1){
			int x,y,k;
			cin>>x>>y>>k;
			change(1,1,n,x,y,k);
		}else{
			int x;
			cin>>x;
			cout<<query(1,1,n,x,x)<<"\n";
		}
	}
	return 0;
} 
5.
luogu P3372 【模板】线段树 1
终于是做上线段树的题了。
思路:
区间加和区间查。
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
int a[N];
struct node{
	int sum,laze;
}tree[N*4];
void pushup(int id){
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
void put_tag(int id,int l,int r,int val){
	tree[id].sum+=1ll*(r-l+1)*val;
	tree[id].laze+=val;
}
void pushdown(int id,int l,int r){
	if(tree[id].laze){
		int mid=(l+r)/2;
		put_tag(id*2,l,mid,tree[id].laze);
		put_tag(id*2+1,mid+1,r,tree[id].laze);
		tree[id].laze=0;
	}
}
void build(int id,int l,int r){
	if(l==r){
		tree[id].sum=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(id*2,l,mid);
	build(id*2+1,mid+1,r);
	tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
long long query(int id,int l,int r,int fl,int fr){
	if(fl==l&&fr==r){
		return tree[id].sum;
	}
	pushdown(id,l,r);
	int mid=(l+r)/2;
	if(fr<=mid){
		return query(id*2,l,mid,fl,fr);
	}else if(fl>mid){
		return query(id*2+1,mid+1,r,fl,fr);
	}else{
		return query(id*2,l,mid,fl,mid)
		+query(id*2+1,mid+1,r,mid+1,fr);
	}
}
void change(int id,int l,int r,int fl,int fr,long long val){
	if(fl==l&&fr==r){
		put_tag(id,l,r,val);
		return ;
	}
	pushdown(id,l,r);
	int mid=(l+r)/2;
	if(fr<=mid){
		change(id*2,l,mid,fl,fr,val);
	}else if(fl>mid){
		change(id*2+1,mid+1,r,fl,fr,val);
	}else{
		change(id*2,l,mid,fl,mid,val);
		change(id*2+1,mid+1,r,mid+1,fr,val);
	}
	pushup(id);
}
signed main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int f;
		cin>>f;
		if(f==1){
			int x,y,k;
			cin>>x>>y>>k;
			change(1,1,n,x,y,k);
		}else{
			int x,y;
			cin>>x>>y;
			cout<<query(1,1,n,x,y)<<"\n";
		}
	}
	return 0;
} 
posted @ 2025-01-20 20:57  lbh123  阅读(34)  评论(0)    收藏  举报