HDU 4348 To the moon(主席树区间修改)
2018-05-23 21:13
363 查看
题意
给你一个区间,支持如下操作:
- 在一段区间内加上一个值,并生成一个历史版本
- 查询某个版本下一段区间内的和
- 回到一个历史版本上并舍弃之后的版本
做法
这就是主席树区间修改裸题啦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 [主席树 区间修改]
- HDU 4348 To the moon 【主席树+区间修改】
- HDU 4348 To the moon (主席树区间更新)
- hdu 4348 To the moon (主席树区间更新)
- hdu-4348-To the moon(主席树更新,区间更新,区间和查询,保存历史版本返回)
- 【HDU - 4348】To the moon(主席树在线区间更新)
- hdu 4348 To the moon 主席树区间更新
- HDU 4348 To the moon(主席树区间更新)
- hdu-4348-To the moon-离线处理 or 主席树区间更新
- hdu-4348-To the moon-主席树在线区间更新
- hdu 4348 To the moon(主席树区间操作)
- HDU 4348 To the moon 主席树 + 区间更新
- hdu 4348 To the moon(主席树,区间更新节省内存,经典)
- HDU 4348 To the moon 可持久化线段树,有时间戳的区间更新,区间求和
- HDU-4348 To the moon(主席树)
- HDU-4348 To the moon (主席树)
- 【HDU4348】To The Moon-主席树(可持久化线段树)区间修改+区间询问
- HDU - 4348 To the moon(主席树)
- vjudge: spoj--to the moon(主席树区间修改)
- HDU 4348 To the moon 主席树