poj-3468

13 篇文章 0 订阅
// 6884K    2032MS  G++
#include "cstdio"
#include "cstring"
#include "iostream"

using namespace std;

#define MAX 100010

struct TreeNode {
    long left;
    long right;
    long long sum;
    long long lazyAdd;
};

TreeNode tree[100010<<2];

long treeSize;
long opNum;

void buildTree(long pos, long begin, long end) {
    TreeNode & curNode = tree[pos];
    curNode.left = begin;
    curNode.right = end;
    curNode.lazyAdd = 0;
    if (begin == end) {
        return;
    } else {
        long mid = (begin + end)>>1;
        buildTree(pos<<1, begin, mid);
        buildTree(pos<<1|1, mid+1, end);
    }
}

void pushUp(long pos) {
    if (pos == 1) {
        return;
    }

    long parentPos = pos>>1;
    TreeNode & leftNode = tree[parentPos<<1];
    TreeNode & rightNode = tree[parentPos<<1|1];
    TreeNode & curNode = tree[parentPos];
    curNode.sum = leftNode.sum + rightNode.sum;
    // printf("pushUp %d %d %lld\n", curNode.left, curNode.right, curNode.sum);
}

void updateSingleNumber(long treePos, long pos, long long val) {
    TreeNode & curNode = tree[treePos];
    long left = curNode.left;
    long right = curNode.right;
    if (left == right && left == pos) {
        curNode.sum = val;
    } else {
        long mid = (left + right)>>1;
        if (pos <= mid) {
            updateSingleNumber(treePos<<1, pos, val);
        } else {
            updateSingleNumber(treePos<<1|1, pos, val);
        }
    }
    pushUp(treePos);
}

void pushDown(long pos) {
    TreeNode & curNode = tree[pos];
    long long curLazyAdd = curNode.lazyAdd;
    if (curLazyAdd != 0) {
        TreeNode & leftNode = tree[pos<<1];
        TreeNode & rightNode = tree[pos<<1|1];
        leftNode.lazyAdd += curLazyAdd;
        rightNode.lazyAdd += curLazyAdd;
        leftNode.sum += curLazyAdd * (leftNode.right - leftNode.left + 1);
        rightNode.sum += curLazyAdd * (rightNode.right - rightNode.left + 1);
        // printf("pushDown %d %d %lld\n", leftNode.left, leftNode.right, leftNode.sum);
        // printf("pushDown %d %d %lld\n", rightNode.left, rightNode.right, rightNode.sum);
        curNode.lazyAdd = 0;
    }
}

long long querySum(long pos, long rangeLeft, long rangeRight) {
    // printf("querySum %d %d\n", rangeLeft, rangeRight);
    TreeNode & curNode = tree[pos];
    long left = curNode.left;
    long right = curNode.right;
    if ((rangeLeft <= left) && (right <= rangeRight)) {
        // printf("querySum1 %d %d %d %d %lld\n", left, right, rangeLeft, rangeRight,curNode.sum);
        return curNode.sum;
    }
    pushDown(pos);

    long mid = (left + right)>>1;
    if (rangeRight <= mid) {
        long long res = querySum(pos<<1, rangeLeft, rangeRight);
        // printf("querySum2 %d %d %d %d %lld\n", left, right, rangeLeft, rangeRight, res);
        return res;
    } else if (rangeLeft<= mid && rangeRight > mid) {
        long long res = querySum(pos<<1, rangeLeft, mid) +
        querySum(pos<<1|1, mid+1, rangeRight);
        // printf("querySum3 %d %d %d %d %lld\n", left, right, rangeLeft, rangeRight, res);
        return res;
    } else if (rangeLeft > mid) {
        long long res = querySum(pos<<1|1, rangeLeft, rangeRight);
        // printf("querySum4 %d %d %d %d %lld\n", left, right, rangeLeft, rangeRight, res);
        return res;
    }
}

void updateRange(long pos, long rangeLeft, long rangeRight, long long addVal) {
    if (!addVal) {
        return;
    }
    TreeNode & curNode = tree[pos];
    long left = curNode.left;
    long right = curNode.right;
    // long long curLazyAdd = curNode.lazyAdd;
    if ((rangeLeft <= left) && (right <= rangeRight)) {
        curNode.sum += addVal * (right - left +1);
        curNode.lazyAdd += addVal;
        // printf("A\n");
        pushUp(pos);
        return;
    }

    // printf("B\n");
    pushDown(pos);

    long mid = (left + right)>>1;
    if (rangeRight <= mid) {
        updateRange(pos<<1, rangeLeft, rangeRight, addVal);
    } else if (rangeLeft <= mid) {
        updateRange(pos<<1, rangeLeft, mid, addVal);
        updateRange(pos<<1|1, mid + 1, rangeRight, addVal);
    } else if (rangeLeft > mid) {
        updateRange(pos<<1|1, rangeLeft, rangeRight, addVal);
    }
    pushUp(pos);
}

int main() {
    while(scanf("%ld %ld", &treeSize, &opNum) != EOF) {
        buildTree(1, 1, treeSize);
        for (int i = 1; i <= treeSize; i++) {
            long long val;
            scanf("%lld", &val);
            updateSingleNumber(1, i, val);
        }
        char tmp;
        // scanf("%c", &tmp);
        for (int i = 1; i <= opNum; i++) {
            char s[5];
            scanf("%s",s);
            if (s[0] == 'C') {
                long left, right;
                long long add;
                scanf("%ld%ld%lld", &left, &right, &add);
                updateRange(1, left, right, add);
            } else if (s[0] == 'Q') {
                long left, right;
                scanf("%ld%ld", &left, &right);
                // printf("%lld\n", querySum(1, left, right));
                cout<<querySum(1, left, right)<<endl;
            }
        }
    }
}


sigh, 线段树区间操作经典题, 此题的AC率低,当时看到,就大概估计是错误的人很多都是数值类型引起的,结果到了自己,也因为这个WA了几次...., lazyAdd的某个中间值没取成long long的64位。

正好刚学完线段树的区间操作和lazytag, 正好在这道题上一用,基本只要心里比较清楚,就能写出正确的code来,中规中矩,

首先就是构造线段树,每个区间节点除了left 和 right外,还要维护两个变量:

该区间的所有数的和 sum 以及 lazyAdd(标记被lazy在此区间节点,没有下发到子区间的要加的数的和)。

一开始要先把所有的初始数值搞进线段树去,直接搞了一个专门的函数,不断递归向下,直到找到正确的位置(一个线段点)放置,然后自下而上更新父区间节点的sum,

然后就是读取若干条指令集了,判断是求某个区间的和 还是 对某个区间的所有数都加上某个数

如果是求某个区间的和,很简单,只需不断根据当前节点的区间和 要求区间的交并情况来递归的对子区间进行求即可,一个很关键的点是:

如果当前在某个区间[l. r], 如果要求的区间和[l1, r2] 不能完全将此区间覆盖, 那么要进行pushDown(将父区间的lazyAdd累加到子区间的lazyAdd,然后父区间的lazyAdd变为0代表在此区间没有被lazy的操作,同时还要对子区间的sum也进行更新,加上lazyAdd * 区间长度,其实就是之前加操作的执行)来将当前区间的lazyAdd下发到其子区间以后,才能继续递归的根据区间交并情况递归向下,还要注意,因为 求区间和这个操作其实没有修改线段树的值,因此不需要pushUp来更新父区间(有些题在这种情况下也需要)


如果是对某个区间进行批量加 S,那么也是根据区间情况不断向下递归求,注意这里要使用lazyTag来提高效率,

如果当前处理到了某个区间[l, r] , 而要批量加的区间是[l1, r1], 如果 [l1, r1]能完全覆盖 [l, r], 那么就不需要再向下递归了,直接先对该区间的sum进行add(S* 区间数的个数), 然后对其lazyTag进行累加即可, 否则根据 [l, r] 在 [l1, r1]的分布情况进行继续的递归处理,注意的是,在每次递归更新某个区间的最后,要调用pushUp来用新的子区间的sum更新父区间的sum,这样才能保证 这次的改变体现在每个被影响的区间上. 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值