题目链接:点击查看

题目大意:给出一个初始时长度为 n 的序列,有 m 次操作,每种操作分为下列四种类型:

  1. C l r d:新建一个继承了前一个版本的数组,并将区间 [ l , r ] 内的数字都加上 d
  2. Q l r:访问最后一个版本的数组,[ l , r ] 内的区间和
  3. H l r t:访问第 t 个版本的数组,[ l , r ] 内的区间和
  4. B t:回溯到第 t 个版本

题目分析:如果不考虑变量 t ,也就是不同版本的数组所带来的影响,那么这就是线段树的一道经典区间修改和区间查询的题目,写一个 lazy 下传标记即可

考虑有多个不同版本的数组,也就对应了主席树,换句话说这个题目考察的正是主席树的区间修改和区间查询

主席树的单点修改比较简单,每次只会涉及到一条链,即 logn 条节点,直接新建,其余的节点继承上一个位置的即可

区间修改的话如果考虑 lazy 变量的下传,最坏的情况下需要从根节点下传到叶子节点,一次操作的时空复杂度都是 nlogn 级别的

那可能有的同学会说了:我就先存一下 lazy 标记,等查询的时候再下传不就行了?

很遗憾的说,这样的时间复杂度单次操作也将会是 nlogn 级别的,因为不同版本的线段树可能 lazy 标记不同,导致 nlogn 个节点的值最终都不相同,所以都需要新建才行

所以这里需要引入一个新的概念,也就是线段树的标记永久化

对于普通线段树的 lazy 标记下传操作,也就是 pushdown 函数,对应过去就是:线段树中每个节点的值都是真实的值

但标记永久化顾名思义,也就是 lazy 标记不下传,这样线段树中的每个节点对应的值此时并不是真实的值,但在执行 query 操作时,一定会从其父节点下来,此时额外维护一个变量,用于记录沿途路径上的 lazy 的贡献,最后统一计算一样可以得到真实答案

但在执行标记永久化时需要注意的细节就是,不能使用 pushup 函数自底向上去传递贡献,只能自顶向下去维护每个节点的贡献,到达需要返回的位置时,打一个 lazy 标记直接返回即可,这样的操作带来的影响是:相应的区间上面的父节点中储存的是真实值,而 lazy 标记下面的那些节点中存在的都是一个虚拟的值,需要与祖先节点中的 lazy 标记的贡献进行加和才能得到真实答案

使用标记永久化的一个好处就是,每次修改仍然是至多修改 logn 个节点,这样就能实现主席树的区间修改的操作了

对于这个题目而言,注意一下空间大小,因为初始时需要建立一棵线段树,这里需要 4 * n ,然后每次修改都需要新建 logn 条树链,设 n 与 m 同阶,最终需要新建 nlogn 个树链,总的空间复杂度也就是 n ( 4 + nlogn ),logn 记为 20,所以开 25 * n 就好了

代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;struct Node
{int l,r;LL sum,lazy;
}tree[N*25];int cnt,root[N];void build(int &k,int l,int r)
{k=cnt++;tree[k].lazy=0;if(l==r){scanf("%lld",&tree[k].sum);return;}int mid=l+r>>1;build(tree[k].l,l,mid);build(tree[k].r,mid+1,r);tree[k].sum=tree[tree[k].l].sum+tree[tree[k].r].sum;
}void update(int &k,int l,int r,int L,int R,LL val)//[l,r]:目标区间 [L,R]:当前区间
{tree[cnt++]=tree[k];k=cnt-1;tree[k].sum+=val*(r-l+1);if(L>=l&&R<=r){tree[k].lazy+=val;return;}int mid=L+R>>1;if(r<=mid)update(tree[k].l,l,r,L,mid,val);else if(l>mid)update(tree[k].r,l,r,mid+1,R,val);else{update(tree[k].l,l,mid,L,mid,val);update(tree[k].r,mid+1,r,mid+1,R,val);}
}LL query(int k,int l,int r,int L,int R,LL add)//[l,r]:目标区间 [L,R]:当前区间
{if(R<l||L>r)return 0;if(L>=l&&R<=r)return tree[k].sum+add*(R-L+1);int mid=L+R>>1;return query(tree[k].l,l,r,L,mid,add+tree[k].lazy)+query(tree[k].r,l,r,mid+1,R,add+tree[k].lazy);
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);int n,m;while(scanf("%d%d",&n,&m)!=EOF){cnt=1;build(root[0],1,n);int time=0;while(m--){char op[5];scanf("%s",op);if(op[0]=='C'){int l,r,d;scanf("%d%d%d",&l,&r,&d);time++;root[time]=root[time-1];update(root[time],l,r,1,n,d);}else if(op[0]=='Q'){int l,r;scanf("%d%d",&l,&r);printf("%lld\n",query(root[time],l,r,1,n,0));}else if(op[0]=='H'){int l,r,t;scanf("%d%d%d",&l,&r,&t);printf("%lld\n",query(root[t],l,r,1,n,0));}else if(op[0]=='B'){scanf("%d",&time);}}}return 0;
}

HDU - 4348 To the moon(主席树区间更新-标记永久化)相关推荐

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

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

  2. hdu 5692 Snacks(dfs序+线段树区间更新)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5692 解题思路:这道题是树节点的点权更新,而且涉及到子树,常用的思路是利用dfs序,用线段树来对区间进 ...

  3. HDU 6356.Glad You Came-线段树(区间更新+剪枝) (2018 Multi-University Training Contest 5 1007)...

    6356.Glad You Came 题意就是给你一个随机生成函数,然后从随机函数里确定查询的左右区间以及要更新的val值.然后最后求一下异或和就可以了. 线段树,区间最大值和最小值维护一下,因为数据 ...

  4. HDU - 4348To the moon——主席树+区间修改

    HDU - 4348To the moon [题目描述] [题目分析] 题目中说明每次更新后时间都会加1,而且还会需要查询以前的区间,还会需要返回以前的时间,所以是很裸的主席树.区间查询的话我们同样需 ...

  5. hdu 1698 Just a Hook 线段树区间更新

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 Let us number the consecutive metallic sticks of ...

  6. hdu 1698 Just a Hook(线段树区间更新·经典)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1698 数据:case,n,q,q行x,y,z.在长度为n的hook上进行q次区间更新,把它们的价值改变.最 ...

  7. 2014多校第四场1006 || HDU 4902 Nice boat (线段树 区间更新)

    题目链接 题意 : 给你n个初值,然后进行两种操作,第一种操作是将(L,R)这一区间上所有的数变成x,第二种操作是将(L,R)这一区间上所有大于x的数a[i]变成gcd(x,a[i]).输出最后n个数 ...

  8. hdu 1556 Color the ball 线段树 区间更新

    水一下 #include <bits/stdc++.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 ...

  9. HDU 4348 To the moon(主席树区间修改)

    题意 给你一个区间,支持如下操作: 在一段区间内加上一个值,并生成一个历史版本 查询某个版本下一段区间内的和 回到一个历史版本上并舍弃之后的版本 做法 这就是主席树区间修改裸题啦QwQ 上一篇博客我讲 ...

最新文章

  1. datatable java实现分页_jQuery Datatable - 使用Php的服务器端处理数据分页
  2. 重设wordpress用户密码
  3. 文巾解题 567. 字符串的排列
  4. python私有成员和保护成员,喜大普奔!Maya 2022来了?!
  5. python中为什么不支持char_python支持char吗
  6. 【编译工具】之gcc中-march与-arch的使用
  7. es5中数组方法unshift、splice、reduce使用
  8. epoll编程实例客户端_网络编程:epoll
  9. 授权windows客户端用户连接Linux mysql命令
  10. matlab 数据透视表,excel表格怎样做数据透视表:如何将excel表格中大量数据导入matlab中并作图...
  11. Retrofit使用教程(一)- Retrofit入门详解
  12. HD、BD、MKV和RMVB、DVD、AVI
  13. 侍魂微信新服务器2019,侍魂胧月传说手游2019年5月14日微信问答试炼答案
  14. 关于Faster-RCNN
  15. 微信输入法,终于来了。。。
  16. 为什么大数据工程师比Java程序员工资高
  17. WPS Office 2019 for Linux(WPS 2019 文字操作指引)
  18. python实现证件照更换背景颜色
  19. C语言——| 和 ||—— 和 的区别用法
  20. 在Docker中使用Python Selenium和Headless Chrome进行网站自动化测试的方法

热门文章

  1. php 修改css 不生效,HTML外部引用CSS文件不生效原因分析及解决办法
  2. java 导出word换行_Java 导出数据库表信息生成Word文档
  3. Dubbo监控平台安装
  4. AOP 中必须明白的概念-切入点(Pointcut)
  5. 缓存-分布式锁-缓存一致性解决
  6. kafka高可用集群原理
  7. Stream流中的常用方法_concat
  8. 异常-根据错误类型捕获异常
  9. h3c服务器 raid 型号,H3C服务器创建Raid
  10. Spring ribbon