Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) |
【思路】
题目要求在区间修改、区间查询的基础上,能够查询历史版本,并可以回退到之前版本。
这就要求我们用可持久化的数据结构,以在规定时间内获得历史版本并且维护它。区间修改和查询用线段树加上懒惰标记即可做,然而在此却不好进行常规的push_down操作,原因有二:1、push_down若向更早版本的低位压,则会导致数据污染,此时查询更早版本就会出错。2、push_down若向当前版本的低位压,就会使得新增加的节点呈现错序排布,此后进行B t操作就不好回收节点了,而且push_down也会生出大量节点,浪费内存。因此,我选用了更省空间写法,利用懒惰标记,当统计[l, r]区间时,在查询它的路上每一层若有mark,都是要给它加上的,最后加上[l, r]自己的sum即可。此外注意,既然不push_down了,也就不能push_up,因为若先改一个大点的区间,再改一个包含在它里面的小区间时,会导致错误的回溯,为保证sum的统一性,我们在递归修改时顺手给高层次的区间加上即可。
此题尤其卡内存,线段树的左右界甚至不能写入节点,而应该用参数传递。
【代码】
//************************************************************************
// File Name: main.cpp
// Author: Shili_Xu
// E-Mail: [email protected]
// Created Time: 2018年02月13日 星期二 13时22分53秒
//************************************************************************
#include
#include
#include
using namespace std;
const int MAXN = 1e5 + 5;
struct segment {
long long sum, mark;
int lson, rson;
};
int n, m, cnt, time;
int a[MAXN], root[MAXN];
segment seg[MAXN * 30];
void build(int left, int right, int &rt)
{
rt = ++cnt;
seg[rt].mark = 0;
int mid = (left + right) >> 1;
if (left == right) {
seg[rt].sum = (long long)a[mid];
return;
}
build(left, mid, seg[rt].lson);
build(mid + 1, right, seg[rt].rson);
seg[rt].sum = seg[seg[rt].lson].sum + seg[seg[rt].rson].sum;
}
void modify(int l, int r, long long num, int left, int right, int pre, int &now)
{
now = ++cnt;
seg[now] = seg[pre];
if (l <= left && right <= r) {
seg[now].sum += num * (right - left + 1);
seg[now].mark += num;
return;
}
int mid = (left + right) >> 1;
if (l <= mid) {
seg[now].sum += num * (min(r, mid) - max(l, left) + 1);
modify(l, r, num, left, mid, seg[pre].lson, seg[now].lson);
}
if (r >= mid + 1) {
seg[now].sum += num * (min(r, right) - max(l, mid + 1) + 1);
modify(l, r, num, mid + 1, right, seg[pre].rson, seg[now].rson);
}
}
long long query(int l, int r, int left, int right, int rt)
{
if (l <= left && right <= r) return seg[rt].sum;
int mid = (left + right) >> 1;
long long ans = 0;
if (left <= l && r <= right) ans += seg[rt].mark * (r - l + 1);
else
if (l <= left && r <= right) ans += seg[rt].mark * (r - left + 1);
else
if (left <= l && right <= r) ans += seg[rt].mark * (right - l + 1);
if (l <= mid) ans += query(l, r, left, mid, seg[rt].lson);
if (r >= mid + 1) ans += query(l, r, mid + 1, right, seg[rt].rson);
return ans;
}
int main()
{
bool flag = false;
while (scanf("%d %d", &n, &m) == 2) {
if (flag)
printf("\n");
else
flag = true;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
cnt = 0; time = 0;
build(1, n, root[0]);
while (m--) {
char mes[2];
scanf("%s", mes);
if (mes[0] == 'C') {
int l, r;
long long d;
scanf("%d %d %lld", &l, &r, &d);
time++;
modify(l, r, d, 1, n, root[time - 1], root[time]);
}
if (mes[0] == 'Q') {
int l, r;
scanf("%d %d", &l, &r);
printf("%lld\n", query(l, r, 1, n, root[time]));
}
if (mes[0] == 'H') {
int l, r, t;
scanf("%d %d %d", &l, &r, &t);
printf("%lld\n", query(l, r, 1, n, root[t]));
}
if (mes[0] == 'B') {
int t;
scanf("%d", &t);
if (t < time) {
cnt = root[t + 1] - 1;
time = t;
}
}
}
}
return 0;
}