标准在线主席树 HDU - 4348【To the moon】

it2022-05-09  31

标准在线主席树 HDU - 4348【To the moon】

https://cn.vjudge.net/contest/304073#problem/G

题意

给定 n 个数和 m 次操作,起始时间为0,操作分为四种:

Q x y —— 询问 [x, y] 区间当前时间节点的和

C x y z —— 把 [x, y] 区间里的所有数加上 z,当前时间节点 +1

H x y z —— 询问 [x, y] 区间在 z 时间节点的和

B x —— 把当前时间节点变成 x

分析

之前做过的几题都属于“权值线段树”(树里每个节点存的值 \(sum\) 表示的是当前数的个数),因此需要进行离散化,用标号表示每个数。

而这一题需要建的树中的每个节点存的值 \(sum\) 就是和常规线段树一样,是真正意义的区间和(因为题目需要求区间和)。

对于区间更新操作来说,我们可以用 lazy 数组。 在从上往下递归的时候,可以把这个父节点的 sum,直接更新。当遇到查询区间和给定区间正好完全重合的时候,我们需要更新 lazy。 这样做的话可以省去 pushdown 的复杂操作。

在查询的时候,我们需要把所有要找的区间 lazy 都加上。

具体操作见代码。

代码

#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <bitset> #include <cctype> #include <cstdio> #include <vector> #include <string> #include <cstdlib> #include <cstring> #include <fstream> #include <iomanip> #include <numeric> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const double PI = acos(-1.0); const double eps = 1e-6; const int inf = 0x3f3f3f3f; const int mod = 1e9 + 7; const int maxn = 1e5 + 5; int n, m, cnt; ll a[maxn]; int root[maxn]; struct node { int l, r; ll sum, lazy; }T[maxn*40]; void init() { cnt = 0; } void build(int l, int r, int &rt) { rt = ++ cnt; T[rt].l = l; T[rt].r = r; T[rt].sum = a[l]; T[rt].lazy = 0; if(l == r) { return ; } int mid = (l+r) / 2; build(l, mid, T[rt].l); build(mid+1, r, T[rt].r); T[rt].sum = T[T[rt].l].sum + T[T[rt].r].sum; } void update(int L, int R, int l, int r, int &x, int y, ll c) { T[++cnt] = T[y]; T[cnt].sum += c*(R-L+1);// 把每个区间的增量都算上 x = cnt; if(L == l && R == r) { T[cnt].lazy += c; // 正好重合时更新lazy标记 return ; } int mid = (l+r) / 2; // 区间更新 if(mid >= R) { update(L, R, l, mid, T[x].l, T[y].l, c); } else if(mid < L) { update(L, R, mid+1, r, T[x].r, T[y].r, c); } else { update(L, mid, l, mid, T[x].l, T[y].l, c); update(mid+1, R, mid+1, r, T[x].r, T[y].r, c); } } ll query(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return T[rt].sum; } ll ans = T[rt].lazy*(R-L+1); // 每个需要找的区间都得加 int mid = (l+r) / 2; if(mid >= R) { ans += query(L, R, l, mid, T[rt].l); } else if(mid < L) { ans += query(L, R, mid+1, r, T[rt].r); } else { ans += query(L, mid, l, mid, T[rt].l); ans += query(mid+1, R, mid+1, r, T[rt].r); } return ans; } int main() { while(~scanf("%d%d", &n, &m)) { init(); for(int i = 1; i <= n; i++) { scanf("%lld", &a[i]); // 非权值线段树,不需要离散化,存的是原数 } build(1, n, root[0]); // cout << T[1].sum << endl; int now_time = 0; for(int i = 1; i <= m; i++) { char f[10]; int x, y, t; ll c; scanf("%s", f); if(f[0] == 'Q') { scanf("%d%d", &x, &y); printf("%lld\n", query(x, y, 1, n, root[now_time])); } else if(f[0] == 'C') { scanf("%d%d%lld", &x, &y, &c); now_time ++; update(x, y, 1, n, root[now_time], root[now_time-1], c); } else if(f[0] == 'H') { scanf("%d%d%d", &x, &y, &t); printf("%lld\n", query(x, y, 1, n, root[t])); } else { scanf("%d", &t); now_time = t; } } } return 0; }

转载于:https://www.cnblogs.com/Decray/p/10930792.html


最新回复(0)