hdu4348 To the moon (主席树区间修改、lazy累加

题意
给一个数组a,有n个数,m次操作。

hdu4348

1e5个数,1e5次查询

操作有四种,
C l r d:更新区间[l,r],值都加上d,并且时间前进1
Q l r:查询[l,r]的区间和
H l r t:查询t时刻的区间和
B t:回到t时刻(一定是历史时刻),且t时刻之后的操作都作废了。

题解
这道题的关键是怎么更新区间和。
本来线段树更新区间和要push down也就是把懒惰标记下移,但是这样,整个修改区间都需要新建线段树的节点,空间不够。
所以考虑在查询的时候传参数下去,也就是一路加上大区间的lazy值,这样就不用push down了。

typedef struct {
    int l, r;
    ll sum, lazy;//sum记录子区间的和、lazy表示所有子区间都要加的数
} node;
node v[N * 32];

需要注意的是lazy的处理,对于不同时间共用的节点我们不能改变其lazy值,因为会对之前的状态产生影响。故不能采用push_down操作。
记录lazy的值,从上往下找的时候累加,找到目标区间时加上即可。

作为主席树区间修改的板子题记录一下。

#include <iostream>
//#include "bits/stdc++.h"
#include "algorithm"
#include "cstring"
#include "vector"
#include "stack"
#include "queue"
#include "set"
#include "cmath"
//#include "unordered_map"

using namespace std;
#define ll long long
//#define int ll
#define double long double
#define inf 0x3f3f3f3f3f3f
#define MOD 10000000000007

#define N 100005
#define MAX 1e9
#define mid ((l+r)>>1)

typedef struct {
    int l, r;
    ll sum, lazy;//sum记录子区间的和、lazy表示所有子区间都要加的数
} node;
node v[N * 32];

int tot, n, m;
ll a[N];
int rt[N];

void push_up(int st, int l, int r) {
    v[st].sum = v[v[st].l].sum + v[v[st].r].sum + (r - l + 1) * v[st].lazy;
}

void build(int &cur, int l, int r) {
    v[cur = ++tot].sum = 0;
    v[cur].lazy = 0;
    if (l == r) {
        v[cur].sum = a[l];
        return;
    }
    build(v[cur].l, l, mid);
    build(v[cur].r, mid + 1, r);
    push_up(cur, l, r);
}

void modify(int &cur, int before, int l, int r, int L, int R, ll p) {
    v[cur = ++tot] = v[before];
    if (L <= l && r <= R) {
        v[cur].lazy += p;
        v[cur].sum += p * (r - l + 1);
        return;
    }
    if (L <= mid) modify(v[cur].l, v[before].l, l, mid, L, R, p);
    if (R > mid) modify(v[cur].r, v[before].r, mid + 1, r, L, R, p);
    push_up(cur, l, r);
}

ll query(int cur, int l, int r, int L, int R, ll p) {
    if (L <= l && r <= R) {
        return v[cur].sum + p * (r - l + 1);
    }
    ll ans = 0;
    if (L <= mid) ans += query(v[cur].l, l, mid, L, R, p + v[cur].lazy);
    if (R > mid) ans += query(v[cur].r, mid + 1, r, L, R, p + v[cur].lazy);
    return ans;
}

void solve() {
    while (cin >> n >> m) {
        tot=0;
        int time = 0;
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        build(rt[0], 1, n);
        for (int i = 1; i <= m; i++) {
            char c;
            cin >> c;
            if (c == 'C') {
                ll d;
                int l,r;
                cin >> l >> r >> d;
                modify(rt[time + 1], rt[time], 1, n, l, r, d);
                time++;
            }
            if (c == 'Q') {
                int l, r;
                cin >> l >> r;
                cout << query(rt[time], 1, n, l, r,0) << endl;
            }
            if (c == 'H') {
                int l, r, t;
                cin >> l >> r >> t;
                cout << query(rt[t], 1, n, l, r,0) << endl;
            }
            if (c == 'B') {
                int t;
                cin >> t;
                time = t;
            }
        }
    }
}


signed main() {
    int tt = 1;
    //cin >> tt;
    while (tt-- > 0) {
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值