线段树及其基本操作

Posted l1l1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树及其基本操作相关的知识,希望对你有一定的参考价值。

处理何种问题:数组单点更新,单点查询,区间更新,区间求和,区间求最值。

 

性能:时间复杂度为O(logn)

 

原理:区间跟新的懒惰标记了解一下,其余略

 

实现步骤:略

 

备注:在线段树里,单点更新与单点累加和树状数组上的单点跟新有区别,树状数组还需与原数组求差值,线段树不用。

线段树的区间求最值差别不大,在此贴一份A过题的最值代码,用来对比找bug。

#include<iostream>

#include<cstdio>

#include<string.h>

#include<algorithm>

using namespace std;

 

const int MaxN=200010;

 

struct node

{

    int l,r,w;

};

node tree[MaxN*4];

int num[MaxN];

int n,ctor,x,ans,a,b,y;

 

void built(int k,int ll,int rr)

{

    tree[k].l=ll;

    tree[k].r=rr;

    if(tree[k].l==tree[k].r)

    {

        tree[k].w=num[ctor++];

    }

    else

    {

        int m=(ll+rr)/2;

        built(k*2,ll,m);

        built(k*2+1,m+1,rr);

        tree[k].w=max(tree[k*2].w,tree[k*2+1].w);

    }

}

 

void change_point(int k)

{

    if(tree[k].l==tree[k].r)

    {

        tree[k].w=y;

    }

    else

    {

        int m=(tree[k].l+tree[k].r)/2;

        if(x<=m)

            change_point(k*2);

        else

            change_point(k*2+1);

        tree[k].w=max(tree[k*2].w,tree[k*2+1].w);

    }

}

void ask_interval(int k)

{

    if(a<=tree[k].l&&tree[k].r<=b)

    {

        ans=max(ans,tree[k].w);

    }

    else

    {

        int m=(tree[k].l+tree[k].r)/2;

        if(a<=m)

            ask_interval(k*2);

        if(b>m)

            ask_interval(k*2+1);

    }

}

 

int main()

{

    int m;

    while(~scanf("%d%d",&n,&m))

    {

        for(int i=1;i<=n;++i)   scanf("%d",&num[i]);

        ctor=1;

        built(1,1,n);

        while(m--)

        {

            char c;

            getchar();

            scanf("%c",&c);

            if(c==‘Q‘)//查询

            {

                scanf("%d%d",&a,&b);

                ans=-100;

                ask_interval(1);

                printf("%d
",ans);

            }

            else if(c==‘U‘)//单点更新

            {

                scanf("%d%d",&x,&y);

                change_point(1);

            }

        }

    }

    return 0;

}

  

 

输入样例解释

9//n个元素

1 2 3 4 5 6 7 8 9

1 5//单点查询为1,x为坐标,从1开始计数

2 5 0//单点修改为2,x为坐标,y为更新值,

3 1 5//区间查询为3,查询[a,b]区间内的元素和,

4 1 5 10//区间更新为4,将[a,b]区间内的元素均加y

输出样例解释

5 //对于1的操作

10//对于3的操作输出

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int MaxN=100010;
struct node
{
    int l,r,w,f;//左边界,右边界,值,懒惰标记
};
node tree[MaxN*4];
int num[MaxN];//原序列
int n,ctor,x,ans,a,b,y;//元素个数,建树是计数变量,单点查询时位置,左区间,右区间,区间修改值

void built(int k,int ll,int rr)//建树
{
    tree[k].l=ll;
    tree[k].r=rr;
    if(tree[k].l==tree[k].r)
    {
        tree[k].w=num[ctor++];
    }
    else
    {
        int m=(ll+rr)/2;
        built(k*2,ll,m);
        built(k*2+1,m+1,rr);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
    }
}
void down(int k)//懒惰标记
{
    tree[k*2].f+=tree[k].f;
    tree[k*2+1].f+=tree[k].f;
    tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
    tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
    tree[k].f=0;
}


void ask_point(int k)//单点查询
{
    if(tree[k].l==tree[k].r)
    {
        ans=tree[k].w;
    }
    else
    {
        if(tree[k].f)   down(k);

        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m)
            ask_point(k*2);
        else
            ask_point(k*2+1);
    }
}

void change_point(int k)//单点更新
{
    if(tree[k].l==tree[k].r)
    {
        tree[k].w+=y;
    }
    else
    {
        if(tree[k].f)   down(k);
        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m)
            change_point(k*2);
        else
            change_point(k*2+1);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
    }
}

void ask_interval(int k)//区间查询
{
    if(a<=tree[k].l&&tree[k].r<=b)
    {
        ans+=tree[k].w;
    }
    else
    {
        if(tree[k].f)   down(k);
        int m=(tree[k].l+tree[k].r)/2;
        if(a<=m)
            ask_interval(k*2);
        if(b>m)
            ask_interval(k*2+1);
    }
}

void change_interval(int k)//区间更新
{
    if(a<=tree[k].l&&tree[k].r<=b)
    {
        tree[k].w+=(tree[k].r-tree[k].l+1)*y;
        tree[k].f+=y;
    }
    else
    {
        if(tree[k].f) down(k);
        int m=(tree[k].l+tree[k].r)/2;
        if(a<=m)
            change_interval(k*2);
        if(b>m)
            change_interval(k*2+1);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
    }
}


int main()
{
    while(~scanf("%d",&n))//n 为元素个数
    {
        for(int i=1; i<=n; ++i)   scanf("%d",&num[i]);
        ctor=1;
        built(1,1,n);

        int m;//操作次数
        scanf("%d",&m);
        while(m--)
        {
            int Q;
            scanf("%d",&Q);
            ans=0;
            if(Q==1)//单点查询
            {
                scanf("%d",&x);
                ask_point(1);
                printf("%d
",ans);
            }
            else if(Q==2)//单点更新
            {
                scanf("%d%d",&x,&y);
                change_point(1);
            }
            else if(Q==3)//区间查询
            {
                scanf("%d%d",&a,&b);
                ask_interval(1);
                printf("%d
",ans);
            }
            else if(Q==4)//区间更新
            {
                scanf("%d%d%d",&a,&b,&y);
                change_interval(1);
            }
        }
    }
    return 0;
}

  

以上是关于线段树及其基本操作的主要内容,如果未能解决你的问题,请参考以下文章

线段树详解

线段树及其应用

[loj3043]线段树

Tyvj 1518 CPU监控——极恶线段树

Codeforces 343D 线段树

线段树+dppoj3171