poj3439 A Simple Problem with Integers 延迟更新的线段树

思路:使用线段树,并且采用延迟标记的方法使更新操作的复杂度控制在O(logn)。这里采用数组的方法存储二叉树,可以省下两个指针的空间,但代码变得不是很直观,而且容易犯错。

另外此题中数据可能超过32位,故使用long long类型存储(POJ平台不能采用__int64,具体原因参见这里),同时使用%lld输出。另外,直接使用scanf读入一个字符的方法会读入空白字符,这里使用scanf("%1s",c);实现。注意因为%s会在输入字符串的末尾加上终止符'\0',故c至少是一个长度为2的数组。

6596kB 600ms 3338 B

#include <stdio.h>
struct InTree{
    int l,u;
    long long v;//使用long long 和输入输入的%lld来保证数据超过32位仍能正常运行
    long long add;//add为延迟标记,标识它的子结点中的每个元素需要增加add
};
InTree tree[3*100000];
//最多有2N-1个节点,最底层最多有N个节点,所以最多占用3N-2个位置。使用tree数组的1~3N-1位置。

//A是输入的数据,i是当前节点在tree数组中的位置,l和u是上下界
void BuildInTree(int *A,int i,int low, int upp){
    tree[i].add = 0;
    if(low==upp){
        tree[i].l = tree[i].u = low;
        tree[i].v = A[low];
        return;
    }
    else{
        tree[i].l = low;
        tree[i].u = upp;
        BuildInTree(A,2*i,low,(low+upp)/2);//左子树的位置为2*i
        BuildInTree(A,2*i+1,(low+upp)/2+1,upp);//右子树的位置为2*i+1
        tree[i].v = tree[2*i].v + tree[2*i+1].v;
    }
}

//更新l到u区间的叶节点值加上v,tree[i]为当前节点,在这里采用延迟标记的方法
void Update(int i,int low, int upp, long long v){
    if(low <= tree[i].l && tree[i].u <= upp){
        tree[i].v += (tree[i].u-tree[i].l+1)*v;//查询到本节点,更新数据
        tree[i].add += v;//更新标记
    }
    else{
    //更新tree[i]的左右的左右子树的标记
        if(tree[i].add !=0){
            //先把标记应用到子结点,再更新子结点的标记
            tree[2*i].v += (tree[2*i].u-tree[2*i].l+1)* tree[i].add;
            tree[2*i].add += tree[i].add;
            tree[2*i+1].v += (tree[2*i+1].u-tree[2*i+1].l+1)* tree[i].add;
            tree[2*i+1].add += tree[i].add;
            tree[i].add = 0;//将本节点的标记置0
        }

        if(low<=(tree[i].l+tree[i].u)/2){
            Update(2*i,low,upp,v);
        }
        if(upp>=(tree[i].l+tree[i].u)/2+1){
            Update(2*i+1,low,upp,v);
        }
        tree[i].v = tree[2*i].v + tree[2*i+1].v;
    }
}

//查询l到u之间的数据和,tree[i]为当前节点
long long Query(int i,int low, int upp){
    if(low <= tree[i].l && tree[i].u <= upp){
        return tree[i].v;
    }
    else{
    //更新tree[i]的左右的左右子树的标记
        if(tree[i].add !=0){
            //先把标记应用到子结点,再更新子结点的标记
            tree[2*i].v += (tree[2*i].u-tree[2*i].l+1)* tree[i].add;
            tree[2*i].add += tree[i].add;
            tree[2*i+1].v += (tree[2*i+1].u-tree[2*i+1].l+1)* tree[i].add;
            tree[2*i+1].add += tree[i].add;

            tree[i].add = 0;//将本节点的标记置0
        }
        long long val = 0;
        if(low<=(tree[i].l+tree[i].u)/2){
            val += Query(2*i,low,upp);
        }
        if(upp>=(tree[i].l+tree[i].u)/2+1){
            val += Query(2*i+1,low,upp);
        }
        return val;
    }
}
int main(){
    int N,Q;
    scanf("%d %d", &N, &Q);
    int *A = new int[N+1];//只使用1~N的区间
    for(int i=1;i<=N;i++)scanf("%d", A+i);
    BuildInTree(A,1,1,N);

    char c[2];
    int l,u;
    long long add;

    for(int i=0;i<Q;i++){
        scanf("%1s",c);//读入一个非空白的字符,注意留空间给%s末尾的'\0'
        if(c[0]=='Q'){
            scanf("%d %d",&l,&u);
            printf("%lld\n",Query(1,l,u));
        }
        else if(c[0]=='C'){
            scanf("%d %d %lld",&l,&u,&add);
            Update(1,l,u,add);
        }
    }
    return 0;
}
3439:A Simple Problem with Integers
查看 提交 统计 提示 提问
时间限制: 5000ms 内存限制: 65536kB
描述
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.


输入
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.


输出
You need to answer all Q commands in order. One answer in a line.


样例输入
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
样例输出
4
55
9
15


posted @ 2012-10-19 00:59  moonswap  阅读(163)  评论(0编辑  收藏  举报