POJ 3468-A Simple Problem with Integers(线段树区间更新)

A Simple Problem with Integers
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 126416 Accepted: 39276
Case Time Limit: 2000MS

Description

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.

Input

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.

Output

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

Sample Input

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

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

Source

既然你写到了线段树区间更新的题,那想必你一定已经写过了线段树单点更新的题,对比起来,区间更新的确比单点更新更加抽象复杂,这个得你自己 静下心来慢慢理解,不急着一下就把它搞定,当你百度的时候你会搜到很多类型的代码,的确,线段树有不同的写法(尽量都理解,会加深你对线段树的深刻理解),但是建树思路是差不多的,这里我的线段树是通过结构体来代替不同的子叶实现的,希望对你的理解有帮助!

解题思路:对于区间更新,你把它和区间查找联系起来,找到目标区间,先把这个区间直接更新,然后它的子区间就不急着去更新了,用个“lazy”来做标记(降低时间复杂度),然后就return,表示上次你更新的时候来过它的父区间,而没更新子区间,后面如果要更新这个子区间的时候(相当于你又要更新一个父区间了,因为它也有子区间啊)加上上次没更新的值(也就是“lazy”里的值)如此下去,就“懒惰的”完成了区间更新hhh!这个“lazy”不仅和更新联系在一起,并且和查找区间的值联系在一起,因为如果你更新的是一个大区间,而要查找的是它的子区间呢,对吧。所以其实在查找的过程中我们也会进行区间更新的操作哦!这样的懒惰更新我们会写一个专门的函数来进行的“Push函数”。接下来就让我们来看看代码吧!(更好的理解这个代码就去看看我 单点更新的代码,想理解就 不要“lazy”哦!)

AC代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<time.h>
#include<map>
#include<string>
#include<algorithm>
#include<set>
#define N 50000

using namespace std;

int a;

struct node
{
    int L; //L-R区间
    int R;
    long long num; //区间值
    long long lazy; //“lazy”标记
    int lenth; //区间长度,用来计算更新的值,因为是区间更新
}leaves[100000*4];

void Push_Tree(int k) //这个就是神秘的更新子区间的Push函数啦!
{
    if(leaves[k].lazy)
    {
        leaves[2*k].num = leaves[2*k].num + leaves[k].lazy*leaves[2*k].lenth; //这里下面两行是更新左右子区间的值
        leaves[2*k+1].num = leaves[2*k+1].num + leaves[k].lazy*leaves[2*k+1].lenth;
        leaves[2*k].lazy += leaves[k].lazy; //下面这两行又是把这个子区间当作一个父区间,进行“lazy标记”
        leaves[2*k+1].lazy += leaves[k].lazy; //不去更新它的子区间
        leaves[k].lazy = 0; //去除“lazy标记”,表示上次我们的懒惰已经解决了!(虽然我们又“lazy”了“子子”区间)
    }
}

void Build_Tree(int l, int r, int k) //建树还是老方式
{
    leaves[k].L = l;
    leaves[k].R = r;
    leaves[k].lazy = 0;
    leaves[k].lenth = r - l + 1;
    if(l == r){
        scanf("%lld", &leaves[k].num);
        return;
    }
    int mid = (l+r)/2;
    Build_Tree(l, mid, 2*k);
    Build_Tree(mid+1, r, 2*k+1);
    leaves[k].num = leaves[2*k].num+leaves[2*k+1].num;
}

void Update_Tree(int l, int r, int add, int k)  //这里我们只要找目标区间进行更新(三种情况)
{                                               //去看我单点更新区间查找的解释,还有图
    if(leaves[k].L == l && leaves[k].R == r){
        leaves[k].num += leaves[k].lenth * add;//更新目标区间的值
        leaves[k].lazy += add;//进行懒惰标记
        return;
    }
    Push_Tree(k); //子区间更新函数,为什么放在这里,可能一下理解不了,你一定要联系代码和数据
    int mid = (leaves[k].L+leaves[k].R)/2; //画图模拟一下,想一下啊(很重要哦!)
    if(r <= mid) Update_Tree(l, r, add, 2*k); //下面是三种情况
    else if(l > mid)Update_Tree(l, r, add, 2*k+1);
    else{
        Update_Tree(l, mid, add, 2*k);
        Update_Tree(mid+1, r, add, 2*k+1);
    }
    leaves[k].num = leaves[2*k].num + leaves[2*k+1].num; //父区间的值等于子区间的值(懒惰的父区间不会进行这个操作)
}                                                        //因为计算过且return了,而且你没有更新它的子区间

long long Search_Tree(int l, int r, int k)
{
    if(leaves[k].L == l && leaves[k].R == r){ //查找同样的三种情况
        return leaves[k].num;
    }
    Push_Tree(k); //我说过,查找的时候也会更新子区间的啦
    int mid = (leaves[k].L+leaves[k].R)/2;
    if(r <= mid) return Search_Tree(l, r, 2*k);
    else if(l > mid) return Search_Tree(l, r, 2*k+1);
    else{
        return Search_Tree(l, mid, 2*k) + Search_Tree(mid+1, r, 2*k+1);
    }
}

int main()
{
    int n, m, x, y, s;
    char oder[2];
    scanf("%d%d", &n, &m);
    Build_Tree(1, n, 1);
    while(m--){
        scanf("%s", oder);
        if(oder[0] == 'C'){
            scanf("%d%d%d", &x, &y, &s);
            Update_Tree(x, y, s, 1);
        }
        if(oder[0] == 'Q'){
            scanf("%d%d", &x, &y);
            printf("%lld\n", Search_Tree(x, y, 1));
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值