POJ 3468 A Simple Problem with Integers(线段树成段更新)

题意:

给出点集数以及命令数;
先将每一个点赋初值
Q a b 表示求出点集中从a到b的和
C a b c 表示将a到b的点全部加c

解析:

线段树,并且单纯的线段树会超时,因为在将a到b的点全部加上c时,步骤太多,会超时。需要优化。即Lazy算法;
Lazy算法:
在将 ql ~ qr 点全部加 val 时,不要加到每个点,在表示区间的 root 结构体上增加一个addv域,将要加的值赋给这个addv域,然后就不要再往下了。在求区间和时,将root中的 addv 值赋给要求的区间,并且将该节点的root置零。

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define ls o*2
#define rs o*2+1
using namespace std;
typedef __int64 ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
ll addv[N<<2], sumv[N<<2], a[N];

inline void maintain(int o, int L, int R) {
    sumv[o] = 0;
    if(R > L) {
        sumv[o] = sumv[ls] + sumv[rs];
    }
    sumv[o] += addv[o] * (R-L+1);
}

void build(int o, int L, int R) {
    if(L == R) {
        sumv[o] = addv[o] = a[L];
        return ;
    }
    int M = (L+R)/2;
    build(ls, L, M);
    build(rs, M+1, R);
    maintain(o, L, R);
}

int ql, qr, val;
void modify(int o, int L, int R) {
    if(ql <= L && R <= qr) {
        addv[o] += val;
    }else {
        int M = (L+R)/2;
        if(ql <= M) modify(ls, L, M);
        if(qr > M) modify(rs, M+1, R);
    }
    maintain(o, L, R);
}

ll _sum;
void query(int o, int L, int R, ll add) {
    if(ql <= L && R <= qr) {
        _sum += sumv[o] + add*(R-L+1);
    }else {
        int M = (L+R)/2;
        if(ql <= M) query(ls, L, M, add+addv[o]);
        if(qr > M) query(rs, M+1, R, add+addv[o]);
    }
}

int main() {
    int n, q;
    char cmd[4];
    while(scanf("%d%d", &n, &q) != EOF) {
        for(int i = 1; i <= n; i++)
            scanf("%I64d", &a[i]);
        build(1, 1, n);
        while(q--) {
            scanf("%s", cmd);
            if(cmd[0] == 'Q') {
                scanf("%d%d", &ql, &qr);
                _sum = 0;
                query(1, 1, n, 0);
                printf("%I64d\n", _sum);
            }else if(cmd[0] == 'C') {
                scanf("%d%d%d", &ql, &qr, &val);
                modify(1, 1, n);
            }
        }
    }
    return 0;
}

你可能感兴趣的:(poj,3468)