线段树入门(二)

上一讲,我们学会了线段树,于是我们就愉(hua)快(ji)地可以暴(da)虐(lian)各大OJ的线段树模板题了。

于是我们就很愉快的打开了某谷oj的模板题。。。

 

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

 

看到这道题我们便很愉快的打上了线段树,一个一个点更新,线段树区间求和,于是便很愉快的TLE了。

你没有看错,线段树这么高效(搞笑)的更新和查询都会TLE。

TLE的原因在于,我们对区间进行区间更新时,使用单点更新,每次复杂度为O(log n),每次最多有n个点要更新,一共有m个操作,总复杂度就为O(m*n*log n),这样肯定会超时的。

怎样解决这个问题呢?我们可以使用延迟标记的思想就可以很好的解决这个问题。

延迟标记:每个节点新增加一个标记,记录这个节点是否进行了某种修改(这种修改操作会影响其子节点),对于任意区间的修改,我们先按照区间查询的方式将其划分成线段树中的节点,然后修改这些节点的信息,并给这些节点标记上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。

这样做的优点在于,不用将区间内的所有值都暴力更新,大大提高效率,因此区间更新是最有用的操作

 

struct segtree
{
    int l,r;
    long long sum,lazy;//lazy表示延迟标记
    void update(long long x)
    {
        sum += (r-l+1)*x;//修改节点的信息
        lazy += x;//标记上这种操作的标记
    }
}tree[maxn<<2];

 

void push_down(int x)//这个函数用于将标记往下传递
{
    int lazyval = tree[x].lazy;//获取延迟标记
    if(lazyval)
    {
        tree[x<<1].update(lazyval);//把标记分别传递给左子节点和右子节点
        tree[x<<1|1].update(lazyval);
        tree[x].lazy = 0;//清空延迟标记
    }
}

 

知道了这两个函数以后我们就可以继续往下学习更新和查询操作了,由于建树的操作一样就不予复述了。

更新操作其实和单点更新差不多,只是多加了一个延迟标记而已。

void update(int x,int l,int r,long long val)
{
    int L=tree[x].l,R=tree[x].r;
    if(l<=L&&R<=r)//当前区间全部在更新区间内
        tree[x].update(val);//直接更新
    else
    {
        push_down(x);//将lazy标记往下传
        int mid = (L+R)>>1;
        if(mid>=l)    update(x<<1,l,r,val);//跟单点更新一样更新左右子节点
        if(r>mid)    update(x<<1|1,l,r,val);
        push_up(x);//自下往上维护线段树
    }
}

查询操作和更新操作基本一样。。。

long long query(int x,int l,int r)//用long long防爆int
{
    int L=tree[x].l,R=tree[x].r;
    if(l<=L&&R<=r)//当前区间全在查询区间内
        return tree[x].sum;
    else
    {
        push_down(x);//传递lazy标记
        long long ans = 0;
        int mid = (L+R)>>1;
        if(mid>=l)    ans += query(x<<1,l,r);//查询左右子节点
        if(r>mid)    ans += query(x<<1|1,l,r);
        push_up(x);//自下往上维护线段树
        return ans;//返回
    }
}

这样我们就真正可以暴虐模板题了。

最后附上模板题完整代码。。。

#include<bits/stdc++.h>

using namespace std;

const int maxn = 100000 +10;
struct segtree
{
    int l,r;
    long long sum,lazy;
    void update(long long x)
    {
        sum += (r-l+1)*x;
        lazy += x;
    }
}tree[maxn<<2];
int a[maxn],n,m;

void push_up(int x)
{
    tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
}

void push_down(int x)
{
    int lazyval = tree[x].lazy;
    if(lazyval)
    {
        tree[x<<1].update(lazyval);
        tree[x<<1|1].update(lazyval);
        tree[x].lazy = 0;
    }
}

void build(int root,int l,int r)
{
    tree[root].l = l;tree[root].r = r;
    if(l==r)
        tree[root].sum = a[l];
    else
    {
        int mid = (l+r)>>1;
        build(root<<1,l,mid);
        build(root<<1|1,mid+1,r);
        push_up(root);
    }
}

void update(int x,int l,int r,long long val)
{
    int L=tree[x].l,R=tree[x].r;
    if(l<=L&&R<=r)
        tree[x].update(val);
    else
    {
        push_down(x);
        int mid = (L+R)>>1;
        if(mid>=l)    update(x<<1,l,r,val);
        if(r>mid)    update(x<<1|1,l,r,val);
        push_up(x);
    }
}

long long query(int x,int l,int r)
{
    int L=tree[x].l,R=tree[x].r;
    if(l<=L&&R<=r)
        return tree[x].sum;
    else
    {
        push_down(x);
        long long ans = 0;
        int mid = (L+R)>>1;
        if(mid>=l)    ans += query(x<<1,l,r);
        if(r>mid)    ans += query(x<<1|1,l,r);
        push_up(x);
        return ans;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    build(1,1,n);
    while(m--)
    {
        int c,x,y,k;
        scanf("%d%d%d",&c,&x,&y);
        if(c==1)
        {
            scanf("%d",&k);
            update(1,x,y,k);
        }
        else
            printf("%lld\n",query(1,x,y));
    }
    return 0;
}
posted @ 2017-07-26 16:11  wdvxdr  阅读(159)  评论(0编辑  收藏  举报