HDU 4348 To the moon(可持久化线段树+内存池)


To the moon

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 7073    Accepted Submission(s): 1651


Problem Description
Background
To The Moon is a independent game released in November 2011, it is a role-playing adventure game powered by RPG Maker.
The premise of To The Moon is based around a technology that allows us to permanently reconstruct the memory on dying man. In this problem, we'll give you a chance, to implement the logic behind the scene.

You‘ve been given N integers A [1], A [2],..., A [N]. On these integers, you need to implement the following operations:
1. C l r d: Adding a constant d for every {A i | l <= i <= r}, and increase the time stamp by 1, this is the only operation that will cause the time stamp increase. 
2. Q l r: Querying the current sum of {A i | l <= i <= r}.
3. H l r t: Querying a history sum of {A i | l <= i <= r} in time t.
4. B t: Back to time t. And once you decide return to a past, you can never be access to a forward edition anymore.
.. N, M ≤ 10 5, |A [i]| ≤ 10 9, 1 ≤ l ≤ r ≤ N, |d| ≤ 10 4 .. the system start from time 0, and the first modification is in time 1, t ≥ 0, and won't introduce you to a future state.
 

Input
n m
A 1 A 2 ... A n
... (here following the m operations. )
 

Output
... (for each query, simply print the result. )
 

Sample Input
 
       
10 51 2 3 4 5 6 7 8 9 10Q 4 4Q 1 10Q 2 4C 3 6 3Q 2 42 40 0C 1 1 1C 2 2 -1Q 1 2H 1 2 1
 
Sample Output
 
       
45591501
 

Author
HIT
 

Source
2012 Multi-University Training Contest 5


【思路】

题目要求在区间修改、区间查询的基础上,能够查询历史版本,并可以回退到之前版本。

这就要求我们用可持久化的数据结构,以在规定时间内获得历史版本并且维护它。区间修改和查询用线段树加上懒惰标记即可做,然而在此却不好进行常规的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;
}

你可能感兴趣的:(线段树)