线段树lazy标记

http://poj.org/problem?id=3468

线段树的核心就是build,query,update,其中update又分为单点更新和区间更新,在区间更新的过程中,如果一路更新到底可能会造成时间消耗很大,而后续的询问并没有用到,这样就造成了资源的浪费,lazy就在这里发挥作用。

在update的过程中如果找到了update的区间(比如2-5),那就把2-5的区间的sum值修改就好了,如果按照正常的思维,这时应该把2-5所在的子区间也全部更新,但是在后续的query中可能并没有用到以下的这些更新值,那么就先不用更新,先用lazy把它存起来。

这样的后果就是在下次询问(query)的过程中,如果有遇到lazy值不为0,也就是有一些更新还没有完成的情况时,就需要把lazy值给释放出来,向下更新一下。

另外在线段树中,区间更新和单点更新得区别就在于update函数,单点更新,位置只能位于一个区间内,而区间更新,这个区间极有可能位于跨度两个区间,这需要注意。

还有写线段树时,pushup和pushdown函数是刚学的,觉得可以让程序更简单易懂,现在渐渐感觉到每个人写程序的风格都是不一样的,一个比较好的算法实现流程可能差不多,但是自己代码的模板是不一样的。

下面的链接是我学习线段树很有力的帮助,博主代码风格简洁,整理的例题也很到位,很喜欢~虽然有些代码还是有些问题……但是适合像我这样萌新(菜鸟),hhh,分享给大家。

http://blog.csdn.net/metalseed/article/details/8039326

POJ3468AC代码

#include<iostream>
using namespace std;

const int maxn = 100005;

struct Tree
{
    int left, right;
    long long sum;
    long long lazy;
}tree[maxn*4];

long long a[maxn];

void pushup(int root)
{
    tree[root].sum = tree[root * 2].sum + tree[root * 2 + 1].sum;
}//向上更新父节点

void pushdown(int root, int m)
{
    if (tree[root].lazy)
    {
        tree[root * 2].lazy += tree[root].lazy;
        tree[root * 2 + 1].lazy += tree[root].lazy;
        tree[root * 2].sum += tree[root].lazy*(m - (m / 2));
        tree[root * 2 + 1].sum += tree[root].lazy*(m / 2);
        tree[root].lazy = 0;
    }
}//向下更新,lazy关键

void build(int root, int start, int end)
{
    tree[root].lazy = 0;
    tree[root].left = start;
    tree[root].right = end;

    if (start == end)
    {
        tree[root].sum = a[start];
        return;
    }

    int mid = (start + end) / 2;
    build(root * 2, start, mid);
    build(root * 2 + 1, mid + 1, end);
    pushup(root);
}

void update(int root,int start,int end,int value)
{
    if (start <= tree[root].left&&tree[root].right <= end)
    //区间的更新,这句话的意思与start == tree[root].left&&tree[root].right == end相同
    {
        tree[root].lazy += value;
        tree[root].sum += (long long)value*(tree[root].right - tree[root].left + 1);
        return;
        //lazy关键
    }

    /*if (tree[root].left == tree[root].right)
        return;*/
    pushdown(root, tree[root].right - tree[root].left + 1);

    int mid = (tree[root].right + tree[root].left) / 2;

    if (end <= mid)
        update(root * 2, start, end, value);
    else
        if (start >= mid + 1)
            update(root * 2 + 1, start, end, value);
        else
        {
            update(root * 2, start, mid, value);
            update(root * 2 + 1, mid + 1, end, value);
        }//区间更新与单点更新的不同就是多了这个else
    pushup(root);
}

long long query(int root,int start,int end)
{
    if (start <= tree[root].left&&tree[root].right <= end)
        return tree[root].sum;
    pushdown(root, tree[root].right - tree[root].left + 1);
    //lazy,然后现在需要调用这个值,就要更新
    int mid= (tree[root].right + tree[root].left) / 2;
    long long reu = 0;
    if (end <= mid)
        reu += query(root * 2, start, end);
    else
        if (start >= mid + 1)
            reu += query(root * 2 + 1, start, end);
        else
            reu = query(root * 2, start, mid) + query(root * 2 + 1, mid+1, end);
    return reu;
}

int main()
{
    int n, q;
    while (cin >> n >> q)
    {
        int i;
        for (i = 1; i <= n; i++)
            cin >> a[i];
        build(1, 1, n);
        while (q--)
        {
            char order;
            int a, b, c;
            cin >> order;
            if (order == 'Q')
            {
                cin >> a >> b;
                cout << query(1, a, b) << endl;
            }
            else
                if(order=='C')
                {
                    cin >> a >> b >> c;
                    update(1, a, b, c);
                }
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值