POJ3468A Simple Problem with Integers(线段树)

最近在学线段树,感觉光做题收获并不是很大,还是要静下来归纳总结写写博客缕清思路。

这是一道线段树区间修改,区间求和的裸题的裸题,参照挑战程序设计书上的方法,维护了一个datb[]数组和一个data[]数据,用来记录整个区间增加的值,从而就没必要把add分配给其孩子结点,节省了一大笔开销。

data 和 datb的主要区别在于修改的区间:[a,b] 是否完全包含当前区间[l,r]。若[l,r] 包含于[a,b] ,那只需 data[] += add , 若[l,r] 不完全包含于[a,b], 那么datb[] += (两个区间的交集)*add。

简而言之: data[k] 记录的是当前区间k是否完整的被加过值。
datb[k]记录的是当前区间部分被加过的值,即当前区间无法执行data[k]+=add时使用datb。

加了datb[k] 就解放了data[k],就使data[k] 的定义从结点k对应区间中所有数之和 转变为 如果只执行结点k及其子孙结点中的add操作,结点k对应区间中所有数之和。

当需要求和的时候,若[a,b]没有完全包含[l,r],则res = [l,r]与[a,b]交集的长度data[k],需要加上之前结点k累计的部分add值,即res = data[k] (当前区间与目标区间的交集),此时不加datb[k]的原因是当前区间并未完全包含于[a,b],datb[k]代表的是当前区间某部分的add值。

所以只有到[l,r]完全包含于[a,b]时,才可以执行 return data[k] *(r-l)+datb[k]。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
#define fre freopen("/Users/user/Desktop/in.txt","r",stdin);
#define CLR(s) memset(s,0,sizeof(s));
#define lson l, (l+r)/2
#define rson (l+r)/2 , r
typedef long long ll;
const int DAT_SIZE = (1<<18)-1;
const int MAX_N = 100000+10;
const int MAX_Q = 100000+10;
int N,Q,A[MAX_N],L[MAX_Q], R[MAX_Q], X[MAX_Q];
char T[MAX_Q];

ll data[DAT_SIZE], datb[DAT_SIZE];

void add(int a,int b, int x, int k, int l,int r){
    if(a <= l && r <= b) // [l,r]包含于[a,b]
        data[k] += x;
    else if(l < b && a < r){
        datb[k] += (min(b,r) - max(a,l))*x; // [a,b] 与 [l,r]相交的区间
        add(a,b,x,k*2+1,lson);
        add(a,b,x,k*2+2,rson);
    }
}

ll sum(int a, int b, int k, int l,int r){
    if(b <= l || r <= a)
        return 0;
    else if(a <= l && r <= b) // [l,r]包含于[a,b]
        return data[k]*(r-l)+datb[k]; // 加上当前结点全部的add值,并且加上之前add操作中对此结点部分区间进行add的值
    else{
        ll res = (min(b,r)-max(a,l))*data[k]; // 加上当前结点交集中的add值,datb先不加。
        res += sum(a,b,k*2+1,lson);
        res += sum(a,b,k*2+2,rson);
        return res;
    }
}

void solve(){
    for(int i = 0; i < N; ++i)
        add(i,i+1, A[i], 0,0,N);
    for(int i = 0; i < Q; ++i)
        if(T[i] == 'C')
            add(L[i],R[i]+1,X[i],0,0,N);
        else
            printf("%lld\n", sum(L[i],R[i]+1,0,0,N));
}

int main(){
    cin >> N >> Q;
    for(int i = 0; i < N; ++i)
        cin >> A[i];
    for(int i = 0; i < Q; ++i){
        cin >> T[i];
        if(T[i] == 'Q')
            cin >> L[i] >> R[i];
        else
            cin >> L[i] >> R[i] >> X[i];
        L[i]--; R[i]--;
    }
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值