poj_3468 线段树

题目大意

    一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和。(这里区间指的是数列中连续的若干个数)对每次询问给出结果。

思路

    对于区间的查找更新操作,可以考虑使用伸展树、线段树等数据结构。这里使用线段树来解决。需要注意的是,对于一个区间的增加操作,如果每次都走到叶子节点进行更新,则必定超时,因此lazy方法来解决。即如果能从当前节点获得所需要的信息,则不必走到子节点。

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
using namespace std;
#define MAX_COUNT 100005
#define MAX(a, b) a>b? a:b
#define MIN(a, b) a<b? a:b

//查找两个相交的区间的并长度(即两个区间覆盖的总长度)
int Interval(int beg1, int end1, int beg2, int end2){
    int tmp = MAX(end1, end2); 
    //注意不要  MAX(end1, end2) -MIN(beg1, beg2), 因为这样做在编译的时候宏定义展开,
    //和三目运算符一块,优先级导致结果出错!!!!

    tmp -= MIN(beg1, beg2);
    return (end1 - beg1 + end2 - beg2) - tmp + 1;
}

int gNumCount, gQueryCount; //总的数字的数目,总的查询次数
    
int gNumber[MAX_COUNT]; //线段树节点的数组

//线段树节点的定义
struct TreeNode{
    int begin;    //该节点覆盖区间的左边界
    int end;    //该节点覆盖区间的右边界

    long long sum;    //该节点覆盖区间的当前sum值,不包括 inc 可能增加的那些值(即实际的和应该为 sum + inc *(end - begin+1)
    long long inc;    //该节点覆盖区间中的那些点 应该被增加的值(注意是该区间的所有点)
};

struct TreeNode gTreeNodes[MAX_COUNT*4];

//自底向上的更新,将左右子的 sum 值相加得到 该节点的sum值
void PushUp(int node_index){
    int left_child = 2 * node_index + 1;
    int right_child = 2 * node_index + 2;
    gTreeNodes[node_index].sum = gTreeNodes[left_child].sum + gTreeNodes[right_child].sum;
}

//自顶向下的更新操作,当该节点不能被查询区间全部覆盖,则需要向下走,去其子节点位置进行查询
//在查询之前,需要将 inc 值传递到子节点,同时该节点的sum值更新,inc值清零
void PushDown(int node_index){
    TreeNode* node = gTreeNodes + node_index;
    int left_child = 2 * node_index + 1;
    int right_child = 2 * node_index + 2;
    node->sum += node->inc*(node->end - node->begin + 1);
    gTreeNodes[left_child].inc += node->inc;
    gTreeNodes[right_child].inc += node->inc;
    node->inc = 0;
}
void BuildTree(int node_index, int beg, int end){
    TreeNode* node = gTreeNodes + node_index;
    node->inc = node->sum = 0;
    node->begin = beg;
    node->end = end;
    if (beg ==  end){
        node->sum = gNumber[beg];
        return;
    }
    int mid = (beg + end) / 2, left_child = 2 * node_index + 1, right_child = 2 * node_index + 2;
    BuildTree(left_child, beg, mid);
    BuildTree(right_child, mid + 1, end);

    //自底向上更新
    PushUp(node_index);
}

void Add(int node_index, int beg, int end, long long c){
    TreeNode* node = gTreeNodes + node_index;
    int left_child = 2 * node_index + 1, right_child = 2 * node_index + 2, mid = (node->begin + node->end) / 2;
    if (node->begin > end || node->end < beg){
        return;
    }
    //如果当前结点被查询区间全部覆盖,则不向下传递,直接将inc值增加
    if (node->begin >= beg && node->end <= end){
        node->inc += c;
        return;
    }
    //如果节点不能被查询区间全部覆盖,则需要分裂节点向下传递,此时的sum值需要增加(inc * 两区间重合的长度)
    //而同时 inc 值保持不变
    node->sum += (Interval(node->begin, node->end, beg, end) * c);
    int end1 = MIN(end, mid);
    int beg1 = MAX(beg, mid + 1);

    Add(left_child, beg, end1, c);
    Add(right_child, beg1, end, c);
}

long long QuerySum(int node_index, int beg, int end){
    TreeNode* node = gTreeNodes + node_index;
//    printf("node %d's sum = %d\n", node_index, gTreeNodes[node_index].sum);
//    printf("node->beg = %d, node->end = %d, beg = %d, end = %d\n", node->begin, node->end, beg, end);

    int left_child = 2 * node_index + 1, right_child = 2 * node_index + 2, mid = (node->begin + node->end) / 2;
    long long sum = 0;
    if (node->begin > end || node->end < beg){
        return sum;
    }
    if (node->begin >= beg && node->end <= end){
        sum  += (node->sum + node->inc*(node->end - node->begin + 1));
    }
    else{
        //向下分裂的更新操作
        PushDown(node_index);

        int end1 = MIN(end, mid);
        int beg1 = MAX(beg, mid + 1);
        long long sum_left = QuerySum(left_child, beg, end1);
        long long sum_right = QuerySum(right_child, beg1, end);
        sum += (sum_left + sum_right);
    }
    return sum;
}

int main(){
    scanf("%d %d", &gNumCount, &gQueryCount);
    for (int i = 0; i < gNumCount; i++){
        scanf("%d", gNumber + i);
    }
    BuildTree(0, 0, gNumCount - 1);

    char op;
    int a, b;
    long long c;
    long long result;
    for (int i = 0; i < gQueryCount; i++){
        getchar();
        scanf("%c", &op);
        if (op == 'C'){
            scanf("%d %d %lld", &a, &b, &c);
            Add(0, a- 1, b-1, c);
            /*
            for (int k = 0; k < 27; k++){
                printf("node[%d]'s sum = %lld, inc = %lld\n", k, gTreeNodes[k].sum, gTreeNodes[k].inc);
            }
            */
        }
        else if (op == 'Q'){
            scanf("%d %d", &a, &b);
            result = QuerySum(0, a-1, b-1);
            printf("%lld\n", result);
        }
    }
    return 0;
}

 

posted @ 2015-08-06 13:41  农民伯伯-Coding  阅读(199)  评论(0编辑  收藏  举报