题意

给你一个区间,支持如下操作:

  • 在一段区间内加上一个值,并生成一个历史版本
  • 查询某个版本下一段区间内的和
  • 回到一个历史版本上并舍弃之后的版本

做法

这就是主席树区间修改裸题啦QwQ

上一篇博客我讲了主席树可以资瓷单点修改,那么区间修改资不资瓷呢?那当然是资瓷的啦。

就像一般的线段树一样,主席树的一个内部节点也可以存储两个儿子的和,在修改时打标记,在查询时直接返回当前区间的值即可。和单点修改不同的是,区间修改一次最多需要修改线段树上的4个节点(可以脑内证明),也就是说我们需要在历史版本上新建 $ 4log_2n $ 个节点。这个过程在单点修改的基础上稍加修改就可以实现,非常简单。

这时候,你可能会发现一个问题:标记下放时,新版本的标记不就下放到旧版本上去了吗(因为被打标记的区间的两个儿子都指向着历史版本)?

一个解决方法是:每一次下放都新建两个节点以防止标记污染到历史版本。这个可行度很高,然而QwQ...这道题的空间只有狗日的64MB!!!这么做空间吃不消啊(╯‵□')╯︵┻━┻

还有一个更直接的方法是:标记根本就不用下放!由于加法标记是可以累加的,在查询时只要把一路上的标记累加起来,再乘以当前区间长度即可。注意,pushup时不能直接让该节点的值等于子节点的值的和,还要加上当前标记乘以区间长度的值,这样才能让当前节点的标记对它上面的节点产生影响。

事实上,标记永久化也可以用在一般的线段树中。但是一定要注意,标记必须要是可累加的且与顺序无关的。比如又有加法又有乘法就不行。

代码

一定要注意输出格式啊QwQ

//By sclbgw7
#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register
using namespace std;
typedef long long LL;
const int MAXN=100005;
int a[MAXN],n; template<class T>int read(T &x)//这是可以判EOF的快速读入
{
x=0;int ff=0;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!=EOF){ff|=(ch=='-');ch=getchar();}
if(ch==EOF)return 0;
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=ff?-x:x;
return 1;
} void readc(char &ch)
{
ch=getchar();
while(ch<'A'||ch>'Z')ch=getchar();
} class CMT
{
private: int cnts[MAXN],root[MAXN],cnt,now; struct CMT_node
{
int l,r,tag;
LL x;
}node[MAXN*30]; void build(int l,int r,int &x)
{
x=++cnt;node[x].tag=0;//一定要清零
if(l==r){node[cnt].x=a[l];return;}
int m=(l+r)>>1;
build(l,m,node[x].l);
build(m+1,r,node[x].r);
node[x].x=node[node[x].l].x+node[node[x].r].x;
} void insert(int st,int en,int l,int r,int &x,int y,int del)
{
x=++cnt,node[x]=node[y];
if(st<=l&&en>=r)
{
node[x].tag+=del;
node[x].x+=(LL)((r-l+1)*del);
return;
}
int m=(l+r)>>1;
if(st<=m)insert(st,en,l,m,node[x].l,node[y].l,del);
if(en>m)insert(st,en,m+1,r,node[x].r,node[y].r,del);
node[x].x=node[node[x].l].x+node[node[x].r].x+(LL)(r-l+1)*node[x].tag; //pushup
} LL query(int st,int en,int l,int r,int x,int tsum)
{
if(st<=l&&en>=r)
{
LL ans=(LL)((r-l+1)*tsum);
ans+=node[x].x;
return ans;
}
int m=(l+r)>>1;LL ans=0;
if(st<=m)ans+=query(st,en,l,m,node[x].l,tsum+node[x].tag);
if(en>m)ans+=query(st,en,m+1,r,node[x].r,tsum+node[x].tag);
return ans;
} public: void init()
{
cnt=now=0;
build(1,n,root[0]);
cnts[0]=cnt;
} void back(int x)//为了节省空间,回退时释放内存
{
now=x;
cnt=cnts[now];
} void change(int l,int r,int del)
{
++now;
insert(l,r,1,n,root[now],root[now-1],del);
cnts[now]=cnt;
} LL getsum(int l,int r,int his,int isnow=0)
{
if(isnow)return query(l,r,1,n,root[now],0);
return query(l,r,1,n,root[his],0);
}
}cmt; int main()
{
int m,fl=0;
while(read(n))
{
if(fl)printf("\n");
else fl=1;
read(m);
for(R int i=1;i<=n;++i)
read(a[i]);
cmt.init();
char ch;
int t1,t2,t3;
for(R int i=1;i<=m;++i)
{
readc(ch);
if(ch=='C')
{
read(t1),read(t2),read(t3);
cmt.change(t1,t2,t3);
}
else if(ch=='Q')
{
read(t1),read(t2);
printf("%lld\n",cmt.getsum(t1,t2,0,1));
}
else if(ch=='H')
{
read(t1),read(t2),read(t3);
printf("%lld\n",cmt.getsum(t1,t2,t3));
}
else
{
read(t1);
cmt.back(t1);
}
}
}
return 0;
}

HDU 4348 To the moon(主席树区间修改)的更多相关文章

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

    传送门 题意: 一个长度为n的数组,4种操作 : (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 . (2)Q l r:查询当前时间戳区间[l,r]中所有数的和 . (3)H ...

  2. hdu 4348 To the moon 主席树区间更新

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

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

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

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

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

  5. HDU 4348 To the moon 主席树

    题意: 给出一个长度为\(n(n \leq 10^5)\)的序列,最开始时间\(t=0\),支持下面几个操作: \(C \, l \, r \, d\):将区间\([l,r]\)每个数都加上\(d\) ...

  6. HDU 4417 Super Mario(主席树 区间不超过k的个数)题解

    题意:问区间内不超过k的个数 思路:显然主席树,把所有的值离散化一下,然后主席树求一下小于等于k有几个就行.注意,他给你的k不一定包含在数组里,所以问题中的询问一起离散化. 代码: #include& ...

  7. hdu 1166 敌兵布阵 线段树区间修改、查询、单点修改 板子题

    题目链接:敌兵布阵 题目: C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视 ...

  8. HDU 5919 Sequence II(主席树+区间不同数个数+区间第k小)

    http://acm.split.hdu.edu.cn/showproblem.php?pid=5919 题意:给出一串序列,每次给出区间,求出该区间内不同数的个数k和第一个数出现的位置(将这些位置组 ...

  9. hdu 4348 To the moon

    题意:n个数 m次操作 操作分别为 C l r d: 把区间[l, r] 加 d Q l r : 查询区间[l, r]的和 H l r t: 查询时间t的时候[l, r]的和 B t: 回到时间t 思 ...

随机推荐

  1. POJ 1182 食物链 (带权并查集)

    食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 78551   Accepted: 23406 Description ...

  2. MT【198】连乘积放缩

    (2018中科大自招最后一题)设$a_1=1,a_{n+1}=\left(1+\dfrac{1}{n}\right)^3(n+a_n)$证明:(1)$a_n=n^3\left(1+\sum\limit ...

  3. 【刷题】BZOJ 2935 [Poi1999]原始生物

    Description 原始生物的遗传密码是一个自然数的序列K=(a1,...,an).原始生物的特征是指在遗传密码中连续出现的数对(l,r),即存在自然数i使得l=ai且r=ai+1.在原始生物的遗 ...

  4. 解决 Previous operation has not finihsed; run ‘cleanup’ if it was interrupted Please execute the ‘Cleanup’ command

    更新时遇到这个问题,解决方法如下: 把根目录下的.svn目录删除掉,再checkout,然后就会出现下面的加version的action.   疯吻IT

  5. LeetCode 8 有效的括号

    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. 注意空字符串可被认 ...

  6. shell中exec命令

    1.find中的-exec参数 在当前目录下(包含子目录),查找所有txt文件并找出含有字符串"bin"的行 find ./ -name "*.txt" -ex ...

  7. 浅谈移动端 View 的显示过程

    作者:个推安卓开发工程师 一七 随着科技的发展,各种移动端早已成为人们日常生活中不可或缺的部分,人们使用移动端产品工作.社交.娱乐……移动端界面的流畅性已经成为影响用户体验的重要因素之一.那么你是否思 ...

  8. Jenkins-Pipeline 流水线发布

    基于docker部署 1.部署jenkins $ yum -y install java $ java -version openjdk version "1.8.0_181" O ...

  9. Spark记录-Scala异常处理与文件I/O

    Scala的异常处理类似许多其他语言(如Java).它不是以正常方式返回值,方法可以通过抛出异常来终止. 但是,Scala实际上并没有检查异常. 当您想要处理异常时,要像Java一样使用try {.. ...

  10. 数学:二次剩余与n次剩余

    二次剩余求的是这个东西 如果给定x,再给定若干个大的质数p,如果结果a相同,那么x是完全平方数? 给出别人的二次剩余的代码: /*poj 1808 题意: 判断平方剩余,即判断(x^2)%p=a是否有 ...