[OI学习笔记]线段树模板

背景

  今天课上讲了树状数组和线段树,有是一脸懵逼。。。

  于是网上有恶补了一下,

  看了几个小时blog终于懂了。。。

  由于很难解释,就只贴两个模板代码(一个单点修改+单点和区间查询,一个区间修改+单点和区间查询)

几点注意事项

  1.首先跑区间更新的程序,一般对于每一个点要维护一个lazy变量来临时存储它将要向下更新的数值,然后对于要查寻的点,直接向下更新,这样偷懒更省时间。(具体实现过程见代码)。

  2.空间要开到4*N(N为元素个数)(好像可以优化成2*N,但是本蒟蒻不知道怎么优化)

  3.打线段树坑点还是很多的,下面列举一下我所犯过的错误:

    1st.睿智地把mid=(tree[k].r+tree[k].l)/2和(tree[k*2].r-tree[k*2].l+1)搞混(全国可能就我会这样了。。。)

    2nd.下放lazy更新子节点的权时,要用父节点的lazy更新,并且区间长度要+1.

    3rd.下放完lazy父节点的lazy值要标位0!!防止重复下放!

  大概就是这些,,,下面直接上代码

代码

  线段树单点修改两种查询:

  

#include<cstdio>
#include<iostream>
using namespace std;

const int MAXN=10010; 

int n;//n个元素,m个操作 

struct node{
    int l,r,v;
}tree[4*MAXN+1];//其实最少开到4*MAXN就行 

void build(int l,int r,int k){//当前节点存储的线段的左右端点&这个节点的编号 
    tree[k].l=l;tree[k].r=r;
    if(l==r){
        scanf("%d",&tree[k].v);//叶子节点的值可在这里读入! 
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);
    tree[k].v=tree[k*2].v+tree[k*2+1].v;
}

//单点查询(叶子节点) 
int askpoint(int x,int k){//目标点在原序列中的下标    k为当前树中的位置下标 
    if(tree[k].l==tree[k].r){
        return tree[k].v;
    }
    int mid=(tree[k].r+tree[k].l)/2;//错点1 //
    if(x<=mid)return askpoint(x,k*2);
    else return askpoint(x,k*2+1);
}

//单点修改 
void changepoint(int x,int num,int k){//要在序列中的第x个数中加上num   k为当前树中的下标 
    if(tree[k].l==tree[k].r){
        tree[k].v+=num;
        return;
    }
    int mid=(tree[k].r+tree[k].l)/2;//错点1 //
    if(x<=mid)changepoint(x,num,k*2);
    else changepoint(x,num,k*2+1);
    tree[k].v=tree[k*2].v+tree[k*2+1].v;//重新整合区间和 
}

//区间查询 
int sum=0;
void asksec(int tl,int tr,int l,int r,int k){
    if(l>=tl&&r<=tr){
        sum+=tree[k].v;
        return;
    }
    int mid=(tree[k].l=tree[k].r)/2;
    if(tl<=mid)asksec(tl,tr,l,mid,k*2);
    if(tr>mid)asksec(tl,tr,mid+1,r,k*2+1);//错点2 不是else// 
}

int main(){
    cout<<"点数"<<endl;//这个程序里不用问题数 
    scanf("%d",&n);
    cout<<"build tree"<<endl; 
    build(1,n,1);
    cout<<"change point"<<endl; 
    int x,num;
    scanf("%d%d",&x,&num);
    changepoint(x,num,1);
    cout<<"ask point"<<endl;
    scanf("%d",&x);
    printf("%d\n",askpoint(x,1));
    cout<<"ask section"<<endl;
    int tl,tr;
    scanf("%d%d",&tl,&tr);
    asksec(tl,tr,1,n,1);
    printf("%d\n",sum);
    return 0;
} 

  线段树区间修改两种查询:

  

#include<cstdio>
#include<iostream>
using namespace std;

const int MAXN=10010; 

int n;//n个元素,m个操作 

struct node{
    int l,r,v,lazy;
}tree[4*MAXN+1];//其实最少开到4*MAXN就行 

void build(int l,int r,int k){//当前节点存储的线段的左右端点&这个节点的编号 
    tree[k].l=l;tree[k].r=r;
    if(l==r){
        scanf("%d",&tree[k].v);//叶子节点的值可在这里读入! 
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);
    tree[k].v=tree[k*2].v+tree[k*2+1].v;
}

//下放lazy标记函数
void putdown(int k){
    tree[k*2].lazy+=tree[k].lazy;
    tree[k*2+1].lazy+=tree[k].lazy;
    tree[k*2].v+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);//坑点:1.要用父节点k的lazy  2.区间长度记得要+1//
    tree[k*2+1].v+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);//坑点:同上//
    tree[k].lazy=0;//坑点:父节点的lazy要清0!!!!!// 
} 

//单点查询(叶子节点) 
int askpoint(int x,int k){
    if(tree[k].l==tree[k].r){
        return tree[k].v;
    }
    if(tree[k].lazy)putdown(k);//防止重复下放 
    int mid=(tree[k].l+tree[k].r)/2;
    if(x<=mid)return askpoint(x,k*2);
    else return askpoint(x,k*2+1);
}

//区间修改 
void changesection(int tl,int tr,int num,int k){
    if(tl<=tree[k].l&&tr>=tree[k].r){//对查询有用 
        tree[k].v+=num*(tree[k].r-tree[k].l+1);//坑点// 
        tree[k].lazy+=num; 
        return;
    }
    if(tree[k].lazy)putdown(k);//防止重复下放 
    int mid=(tree[k].r+tree[k].l)/2;//错点 //
    if(tl<=mid)changesection(tl,tr,num,k*2);
    if(tr>mid)changesection(tl,tr,num,k*2+1);
    tree[k].v=tree[k*2].v+tree[k*2+1].v;
}

//区间查询 
int sum=0;
void asksec(int tl,int tr,int l,int r,int k){
    if(l>=tl&&r<=tr){
        sum+=tree[k].v;
        return;
    }
    if(tree[k].lazy)putdown(k);//防止重复下放 
    int mid=(tree[k].r+tree[k].l)/2;
    if(tl<=mid)asksec(tl,tr,l,mid,k*2);
    if(tr>mid)asksec(tl,tr,mid+1,r,k*2+1);
}

int main(){
    freopen("in.in","r",stdin);
    cout<<"点数"<<endl;
    scanf("%d",&n);
    cout<<"build tree"<<endl; 
    build(1,n,1);
    cout<<"change section"<<endl; 
    int x,y,num;
    scanf("%d%d%d",&x,&y,&num);
    changesection(x,y,num,1);
    cout<<"ask point"<<endl;
    scanf("%d",&x);
    printf("%d\n",askpoint(x,1));
    cout<<"ask section"<<endl;
    int tl,tr;
    scanf("%d%d",&tl,&tr);
    asksec(tl,tr,1,n,1);
    printf("%d\n",sum);
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/sjrb/p/10340005.html
今日推荐