传送门

To the moon

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

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, \dots, 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 \mid l \le i \le 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 \mid l \le i \le r\}$.
3. H l r t: Querying a history sum of {Ai | 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 ≤ 105, |A[i]| ≤ 109, 1 ≤ l ≤ r ≤ N, |d| ≤ 104 .. 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
A1 A2 ... An
... (here following the m operations. )
 

Output

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

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

2 4
0 0
C 1 1 1
C 2 2 -1
Q 1 2
H 1 2 1

Sample Output

4
55
9
15

0
1

Author

HIT

Source


Solution

裸的可持久化线段树,但卡空间,只给 64 M。这是我写的第 3 道可持久化线段树的题目。
这篇随笔主要讨论:可持久化线段树在维护区间更新(相对于单点更新而言)操作,需要打懒标记(lazy-tag,以下简称“标记”或“tag”)时如何节省空间(内存)。
另外这个题目还涉及到一个“时间倒流”的操作——回到线段树的某个之前版本并删除在此之后的所有版本,我们也简单讨论下在这个操作后如何回收后续版本占用的空间
 
可持久化数据结构用于维护某个数据结构的不同历史版本,在逻辑上我们应当认为这些不同的version属于同一个object。一般来说,一个数据结构由若干元素构成,可持久化数据结构的思想是:相邻两个版本共用相同元素,在后一版本中只新建被更改了的元素。线段树(属于二叉树)的元素就是节点,节点维护着某个区间的信息。
 
当我们修改一个节点时当然要新建一个节点作为该节点的当前版本;如果该节点上有标记,非可持久化线段树的写法是:先将该标记push-down到两个子节点上,然后把当前节点的标记消除,最后再从两个子节点push-up来更新该节点。如果可持久化线段树也按这种方式处理标记,那么每次push-down就需要新建两个子节点,这两个子节点是和修改之前的父节点同一版本的,而不是当前版本的(“当前版本”是指修改之后的父节点的版本)。这样在query和update的过程中,push-down会新建大量节点,而且这么做还有一个弊端——它导致属于同一版本的新建节点在数组中不连续,这一点后面还会谈到。
 
对于标记还有另为一种处理方法——Never push down
在 query 或 update 的过程中不 push-down 任何节点的 tag,查询时,如果当前节点有标记,就把该标记对要查询的区间 $[l, r]$ 和该节点表示的区间 $[L, R]$ 的交集 $[\max(l, L), \min(r, R)]$ 的贡献更新到答案中去。
 
这么做的效果就是:
  1. 只有被修改了的节点才会被新建,而这正是我们所期望的。(注意:从逻辑上讲,上个方法中 push-down 新建的那两个子节点在之前的某个本中就存在了,只是没有建出来。)
  2. 属于同一版本的新建节点在数组中是连续的,而且显然相邻版本的新建节点也是相邻的。

这样对于回到 $t$ 时刻的“时间倒流”操作,只要把数组的 $\mathrm{tail}$ 直接置成 $\mathrm{tail}_t$ 就好了。($t$ 时刻内新建的节点在 $[\mathrm{tail}_{t-1}, \mathrm{tail}_t)$ 区间内)

Implementation

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+, M=2.5e6+;
typedef long long LL;
int ls[M], rs[M], tail, now, root[N];
LL add[M], sum[M]; void push_up(int id){
sum[id]=sum[ls[id]]+sum[rs[id]];
} int get_cur(int id, int now){
return tail++;
} LL query(int id, int L, int R, int l, int r){
if(l>R || L>r) return ;
if(l<=L && R<=r) return sum[id];
int mid=L+R>>;
return add[id]*(min(R, r)-max(L, l)+)+query(ls[id], L, mid, l, r)+query(rs[id], mid+, R, l, r);
} int Add(int id, int L, int R, int l, int r, int v){
if(l>R || L>r) return id;
int cur=get_cur(id, now); add[cur]=add[id], sum[cur]=sum[id]+(min(R, r)-max(L, l)+)*v; //copy tag only if(l<=L && R<=r){
add[cur]+=v;
ls[cur]=ls[id], rs[cur]=rs[id];
} else{
int mid=L+R>>;
ls[cur]=Add(ls[id], L, mid, l, r, v);
rs[cur]=Add(rs[id], mid+, R, l, r, v);
}
return cur;
} void init(int id, int L, int R){
add[id]=;
if(L==R){
scanf("%lld", sum+id);
return;
}
int mid=L+R>>;
init(ls[id]=tail++, L, mid);
init(rs[id]=tail++, mid+, R);
push_up(id);
} int main(){
// cout<<M<<endl;
for(int n, m; cin>>n>>m; ){
now=, tail=, init(root[now]=tail++, , n);
char op[];
int l, r, d, t; for(; m--; ){
scanf("%s", op);
if(*op=='C'){
scanf("%d%d%d", &l, &r, &d);
++now, root[now]=Add(root[now-], , n, l, r, d); //error-prone
}
else if(*op=='Q'){
scanf("%d%d", &l, &r);
printf("%lld\n", query(root[now], , n, l, r));
}
else if(*op=='H'){
scanf("%d%d%d", &l, &r, &t);
printf("%lld\n", query(root[t], , n, l, r)); //error-prone
}
// you can never access a forward edition anymore.
else scanf("%d", &t), now=t;
}
}
}
 
 

HDU 4388 To the moon的更多相关文章

  1. HDU 4383 To The Moon 解题报告

    HDU 4383 To The Moon 题意翻译 已知一个长为\(n\)的序列\(a\),你需要进行下面的四种操作. C l r d 将区间\([l,r]\)中的数加上\(d\),同时时间加\(1\ ...

  2. hdu 4388 Stone Game II

    Stone Game II HDU - 4388 题目大意: 给出n堆物品,每堆物品都有若干件,现在A和B进行游戏,每人每轮操作一次,按照如下规则: 1. 任意选择一个堆,假设该堆有x个物品,从中选择 ...

  3. HDU 4348.To the moon SPOJ - TTM To the moon -可持久化线段树(带修改在线区间更新(增减)、区间求和、查询历史版本、回退到历史版本、延时标记不下放(空间优化))

    To the moon Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total ...

  4. HDU 4388 Stone Game II 博弈论 找规律

    http://acm.hdu.edu.cn/showproblem.php?pid=4388 http://blog.csdn.net/y1196645376/article/details/5214 ...

  5. HDU 4348 To the moon 可持久化线段树

    To the moon Problem Description BackgroundTo The Moon is a independent game released in November 201 ...

  6. HDU 4348 To the moon 可持久化线段树,有时间戳的区间更新,区间求和

    To the moonTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/view.a ...

  7. hdu 4348 To the moon (主席树 区间更新)

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=4348 题意: 4种操作: C l r c   区间[l,r]加c,时间+1 Q l r    询问当前时 ...

  8. hdu 4348 To the moon (主席树)

    版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 4348 题意: 一个长度为n的数组,4种操作 : (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 . (2)Q ...

  9. HDU 4348 To the moon 主席树 在线更新

    http://acm.hdu.edu.cn/showproblem.php?pid=4348 以前做的主席树没有做过在线修改的题做一下(主席树这种东西正经用法难道不是在线修改吗),标记永久化比较方便. ...

随机推荐

  1. 【软件编程】乐易贵宾VIP教程 - JS改写+网页操作系列教程

    JS改写系列教程: 1.MD5加密改写教程(爱拍网登录)2.解密如何快速找到真确的js加密算法3.多重MD5加密改写教程(5173登录)4.DZ论坛登录加密改写5.唯品会手机登录加密改写6.新浪微博密 ...

  2. ASP.NET + SqlSever 大数据解决方案 PK HADOOP

    半个月前看到博客园有人说.NET不行那篇文章,我只想说你们有时间去抱怨不如多写些实在的东西.  1.SQLSERVER优点和缺点? 优点:支持索引.事务.安全性以及容错性高 缺点:数据量达到100万以 ...

  3. ModernUI教程:如何从MUI样式中派生自定义样式

    下面的步骤用来说明怎么样去创建一个基于MUI的自定义样式.让我们创建一个字体颜色显示为红色的按钮样式. 可视化显示如下: 因为我们并没有明确生命继承自MUI风格,它还是采用WPF的默认风格.我们需要设 ...

  4. [BZOJ3696][FJSC2014]化合物(异或规则下的母函数)

    题目:http://hzwer.com/3708.html 分析: 类似树分治思想,设f[x][i]表示以x为根的子树的所有点中,与x的距离为i的点有多少个,这个可以预处理出来 然后我们考虑每颗子树对 ...

  5. struct socket 结构详解

    Socket数据结构网络协议CC++     用户使用socket系统调用编写应用程序时,通过一个数字来表示一个socket,所有的操作都在该数字上进行,这个数字称为套接字描述符.在系统调用 的实现函 ...

  6. android 开发之 百度地图的使用

    好久没写博客了,最近遇到个新需求 需要用到百度地图的基础地图,定位,理论上应该还会用到鹰眼的功能吧.具体还很难说.我现在 刚动工,就从头开始记录吧. 首先是先申请一个百度地图api的key 流程官网很 ...

  7. Android复习笔记--Intent

    Intent是Android中各组件跳转的重要方式,一般可悲用于启动活动.启动服务.以及发送广播等场景. #显示Intent 主要主要用于启动已知的组件 //发送方  Intent intent = ...

  8. 屠龙之路_假期罢工和公主私奔_SixthDay

    摘要:屠龙少年经过一周的长途跋涉后,终于来到了传说中的周末客栈.周末客栈是屠龙之路的必经之地,屠龙少年可以在周末客栈补给干粮,修补装备,好好休息一下,以便更好的上路.周末客栈有个不成文的规定:凡入住者 ...

  9. 十天冲刺---Day8

    站立式会议 站立式会议内容总结: 燃尽图 照片 最近思考一个问题.项目是怎么进行到这一步的. 算了,这个发在明天的冲刺总结吧.. 还需继续努力,队友快回来快回来..

  10. 一个Activity掌握Android5.0新控件 (转)

    原文地址:http://blog.csdn.net/lavor_zl/article/details/51279386 谷歌在推出Android5.0的同时推出了一些新控件,Android5.0中最常 ...