线段树秀操作题。

奇怪的计算器

有 N 个数,一共会对这 N 个数执行 M 个指令(对没个数执行的指令都一样),每一条指令可以是以下四种指令之一:(这里 a 表示一个正整数)

  1. 加上 a
  2. 减去 a
  3. 乘以 a
  4. 加上 a*X(X 是数最开始的初值)

该计算器有个奇怪的特点。每进行一个指令,若结果大于 R则变成 R,同理若结果小于 L,则变成 L。求这 N 个数最后的结果。

N, M ≤ 200000

题解

重要性质:无论题目里面的修改怎么执行,所有数的相对大小(即排名)不变。所以我们每次的chkmin和chkmax操作肯定是对一个排序后的后缀和前缀区间进行的。那么我们成功地将不连续的问题转化成了连续的问题。

所以我们可以针对题目设计懒标记 x=t1*x+t2*x0+t3。懒标记的合并很显然,而由于排名不变,所以区间的最大最小值还是很好维护的。

时间复杂度 O(n log n)。

  1. CO int N=100000+10;
  2. int L,R;
  3. pair<int,int> p[N],q[N];
  4. LL t1[4*N],t2[4*N],t3[4*N];
  5. LL lp[4*N],rp[4*N],mn[4*N],mx[4*N];
  6. #define lc (x<<1)
  7. #define rc (x<<1|1)
  8. void build(int x,int l,int r){
  9. t1[x]=1,t2[x]=t3[x]=0;
  10. lp[x]=mn[x]=q[l].first,rp[x]=mx[x]=q[r].first;
  11. if(l==r) return;
  12. int mid=(l+r)>>1;
  13. build(lc,l,mid),build(rc,mid+1,r);
  14. }
  15. IN void push_up(int x){
  16. mn[x]=mn[lc],mx[x]=mx[rc];
  17. }
  18. IN void add_tag(int x,LL k1,LL k2,LL k3){
  19. t1[x]*=k1,t2[x]=k1*t2[x]+k2,t3[x]=k1*t3[x]+k3;
  20. mn[x]=k1*mn[x]+k2*lp[x]+k3,mx[x]=k1*mx[x]+k2*rp[x]+k3;
  21. }
  22. IN void push_down(int x){
  23. if(t1[x]!=1 or t2[x] or t3[x]){
  24. add_tag(lc,t1[x],t2[x],t3[x]);
  25. add_tag(rc,t1[x],t2[x],t3[x]);
  26. t1[x]=1,t2[x]=t3[x]=0;
  27. }
  28. }
  29. void chkl(int x){
  30. if(mn[x]>=L) return;
  31. if(mx[x]<=L) return add_tag(x,0,0,L);
  32. push_down(x);
  33. if(mx[lc]<=L) add_tag(lc,0,0,L),chkl(rc);
  34. else chkl(lc);
  35. push_up(x);
  36. }
  37. void chkr(int x){
  38. if(mx[x]<=R) return;
  39. if(mn[x]>=R) return add_tag(x,0,0,R);
  40. push_down(x);
  41. if(mn[rc]>=R) add_tag(rc,0,0,R),chkr(lc);
  42. else chkr(rc);
  43. push_up(x);
  44. }
  45. int ans[N];
  46. void query(int x,int l,int r){
  47. if(l==r){
  48. ans[q[l].second]=mn[x];
  49. return;
  50. }
  51. push_down(x);
  52. int mid=(l+r)>>1;
  53. query(lc,l,mid),query(rc,mid+1,r);
  54. }
  55. int main(){
  56. int n=read<int>();
  57. read(L),read(R);
  58. for(int i=1;i<=n;++i){
  59. char opt[2];scanf("%s",opt);
  60. if(opt[0]=='+') p[i].first=1;
  61. else if(opt[0]=='-') p[i].first=2;
  62. else if(opt[0]=='*') p[i].first=3;
  63. else p[i].first=4;
  64. read(p[i].second);
  65. }
  66. int m=read<int>();
  67. for(int i=1;i<=m;++i) read(q[i].first),q[i].second=i;
  68. sort(q+1,q+m+1);
  69. build(1,1,m);
  70. for(int i=1;i<=n;++i){
  71. if(p[i].first==1) add_tag(1,1,0,p[i].second);
  72. else if(p[i].first==2) add_tag(1,1,0,-p[i].second);
  73. else if(p[i].first==3) add_tag(1,p[i].second,0,0);
  74. else add_tag(1,1,p[i].second,0);
  75. if(mx[1]>R) chkr(1);
  76. if(mn[1]<L) chkl(1);
  77. }
  78. query(1,1,m);
  79. for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
  80. return 0;
  81. }

Gorgeous Sequence

There is a sequence a of length n. We use ai to denote the i-th element in this sequence. You should do the following three types of operations to this sequence.

0 x y t: For every x≤i≤y, we use min(ai,t) to replace the original ai's value.

1 x y: Print the maximum value of ai that x≤i≤y.

2 x y: Print the sum of ai that x≤i≤y.

分析

吉司机线段树板子题。

1和2操作很简单,0操作很难,一个一个改时间肯定不允许,但是又没什么办法可以直接全改掉。

结合题目问的是区间最大值和区间和,那么就思考操作 0 会对区间最大值和区间和有什么影响,于是就有了以下方法:

维护 3 个值,区间最大值,区间严格次大值,区间最大值的个数。然后我们每次做0操作的时候,就会有3种情况。

  1. t >= 区间最大值, 这时每个值都不用修改,直接返回。
  2. 区间次大值 < t < 区间最大值,此时只有最大值会变,又已经求得了最大值的个数,所以我们可以直接更新这段的sum和max。
  3. 其他情况。无法直接对当前情况修改,所以继续搜两个儿子,直到搜到前两种情况为止。

我们可以把区间的最大值看做是这个区间的标记,因为每次更新的时候只会更新到(区间次大值 < t < 区间最大值)的情况,然后会修改sum和max,所以以后一旦进入这个区间的子树,必须先更新这个子树的max和sum。于是就有了pushdown()的写法,它就是负责把当前区间的sum和mx信息传给子树。

  1. const int MAXN=1e6+7;
  2. int ql,qr,v;
  3. struct SegTree
  4. {
  5. ll sum[MAXN<<2];
  6. int maxv[MAXN<<2],num[MAXN<<2],secv[MAXN<<2];
  7. #define lson (now<<1)
  8. #define rson (now<<1|1)
  9. inline void pushup(int now)
  10. {
  11. sum[now]=sum[lson]+sum[rson];
  12. maxv[now]=max(maxv[lson],maxv[rson]);
  13. if(maxv[lson]==maxv[rson])
  14. {
  15. num[now]=num[lson]+num[rson];
  16. secv[now]=max(secv[lson],secv[rson]);
  17. }
  18. else
  19. {
  20. num[now]=maxv[lson]>maxv[rson]?num[lson]:num[rson];
  21. secv[now]=max(secv[lson],secv[rson]);
  22. secv[now]=max(secv[now],min(maxv[lson],maxv[rson]));
  23. }
  24. }
  25. void build(int now,int l,int r)
  26. {
  27. if(l==r)
  28. {
  29. sum[now]=_read();
  30. maxv[now]=sum[now];
  31. num[now]=1;
  32. secv[now]=-1;
  33. return;
  34. }
  35. int mid=(l+r)>>1;
  36. build(lson,l,mid);
  37. build(rson,mid+1,r);
  38. pushup(now);
  39. }
  40. inline void puttag(int now,int x)
  41. {
  42. if(x>=maxv[now])
  43. return;
  44. sum[now]-=(ll)num[now]*(maxv[now]-x);
  45. maxv[now]=x;
  46. }
  47. inline void pushdown(int now)
  48. {
  49. puttag(lson,maxv[now]);
  50. puttag(rson,maxv[now]);
  51. }
  52. void change(int now,int l,int r)
  53. {
  54. if(v>=maxv[now])
  55. return;
  56. if(ql<=l&&r<=qr&&secv[now]<v)
  57. {
  58. puttag(now,v);
  59. return;
  60. }
  61. pushdown(now);
  62. int mid=(l+r)>>1;
  63. if(ql<=mid)
  64. change(lson,l,mid);
  65. if(qr>=mid+1)
  66. change(rson,mid+1,r);
  67. pushup(now);
  68. }
  69. ll qmax(int now,int l,int r)
  70. {
  71. if(ql<=l&&r<=qr)
  72. {
  73. return maxv[now];
  74. }
  75. pushdown(now);
  76. int mid=(l+r)>>1;
  77. ll ans=0;
  78. if(ql<=mid)
  79. ans=max(ans,qmax(lson,l,mid));
  80. if(qr>=mid+1)
  81. ans=max(ans,qmax(rson,mid+1,r));
  82. return ans;
  83. }
  84. inline ll qsum(int now,int l,int r)
  85. {
  86. if(ql<=l&&r<=qr)
  87. {
  88. return sum[now];
  89. }
  90. pushdown(now);
  91. int mid=(l+r)>>1;
  92. ll ans=0;
  93. if(ql<=mid)
  94. ans+=qsum(lson,l,mid);
  95. if(qr>=mid+1)
  96. ans+=qsum(rson,mid+1,r);
  97. return ans;
  98. }
  99. }Tree;
  100. int n,m;
  101. int main()
  102. {
  103. // freopen(".in","r",stdin);
  104. // freopen(".out","w",stdout);
  105. int T=_read();
  106. while(T--)
  107. {
  108. n=_read();m=_read();
  109. Tree.build(1,1,n);
  110. while(m--)
  111. {
  112. int opt=_read();
  113. if(opt==0)
  114. {
  115. ql=_read();qr=_read();v=_read();
  116. Tree.change(1,1,n);
  117. }
  118. else if(opt==1)
  119. {
  120. ql=_read();qr=_read();
  121. printf("%lld\n",Tree.qmax(1,1,n));
  122. }
  123. else if(opt==2)
  124. {
  125. ql=_read();qr=_read();
  126. printf("%lld\n",Tree.qsum(1,1,n));
  127. }
  128. }
  129. }
  130. // fclose(stdin);
  131. // fclose(stdout);
  132. return 0;
  133. }

BZOJ4695 最假女选手是这道题的复杂版。

AHOI2014 奇怪的计算器 和 HDU5306 Gorgeous Sequence的更多相关文章

  1. hdu5306 Gorgeous Sequence

    hdu5306 Gorgeous Sequence 题目大意 ​ 给你一个序列,维护区间和,区间chkmin和区间最大值 数据范围 数据组数T,序列长度n,操作次数m $T = 100,\sum n ...

  2. BZOJ 3878: [Ahoi2014]奇怪的计算器

    BZOJ 3878: [Ahoi2014]奇怪的计算器 标签(空格分隔): OI-BZOJ OI-线段树 Time Limit: 10 Sec Memory Limit: 256 MB Descrip ...

  3. [HDU5306]Gorgeous Sequence(标记回收线段树)

    题意:维护一个序列,支持区间与一个数取min,询问区间最大,询问区间和(序列长度<=1e6) 分析: http://www.shuizilong.com/house/archives/hdu-5 ...

  4. BZOJ3878: [Ahoi2014&Jsoi2014]奇怪的计算器

    BZOJ3878: [Ahoi2014&Jsoi2014]奇怪的计算器 Description [故事背景] JYY有个奇怪的计算器,有一天这个计算器坏了,JYY希望你能帮助他写 一个程序来模 ...

  5. 「AHOI2014/JSOI2014」奇怪的计算器

    「AHOI2014/JSOI2014」奇怪的计算器 传送门 我拿到这题首先是懵b的,因为感觉没有任何性质... 后来经过同机房dalao的指导发现可以把所有的 \(X\) 放到一起排序,然后我们可以发 ...

  6. BZOJ 3878 【AHOI2014】 奇怪的计算器

    题目链接:奇怪的计算器 如果没有溢出的话,所有的标记都可以在线段树上直接维护,所以一棵线段树就解决问题了. 现在有了溢出,怎么办呢? 发现就算溢出了,各个元素的相对大小关系也是不变的.所以,如果一开始 ...

  7. HDU 5306 Gorgeous Sequence[线段树区间最值操作]

    Gorgeous Sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  8. 2015 Multi-University Training Contest 2 hdu 5306 Gorgeous Sequence

    Gorgeous Sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  9. HDOJ 5306 Gorgeous Sequence 线段树

    http://www.shuizilong.com/house/archives/hdu-5306-gorgeous-sequence/ Gorgeous Sequence Time Limit: 6 ...

随机推荐

  1. YYCache 的整体架构类图

  2. 【CSP2019】题解合集

    诈个尸 先挖坑 虽然连去都没去但还是想做做 今年貌似比去年还毒瘤啊... yrx.hjw都进了省队线tql orz (myh:没AK真丢脸 Day1T1 格雷码 Day1T2 括号树 Day1T3 树 ...

  3. 由一个问题引起的思考:WEB开发中,使用JSON-RPC好,还是RESTful API好?

    起因: 研究zabbix的API设计风格.查看zabbix官网API文档,可以看到使用的是json-rpc:2.0 随后搜索到知乎上的一个问题讨论:https://www.zhihu.com/ques ...

  4. kubectl 创建 Pod 背后到底发生了什么?

    原文链接:kubectl 创建 Pod 背后到底发生了什么? 想象一下,如果我想将 nginx 部署到 Kubernetes 集群,我可能会在终端中输入类似这样的命令: $ kubectl run - ...

  5. servlet是一组规范--Servlet是JavaEE规范的一种

    Java Servlet API是Servlet容器和Servlet之间的接U,它定义了Servlet的各种方法, 还定义了Servlet容器传送给Servlet的对象类,其中最重要的是请求对象Ser ...

  6. - 反编译 AndroidKiller 逆向 实践案例 MD

    目录 目录 反编译 AndroidKiller 逆向 实践案例 MD AndroidKiller 简介 插件升级 基本使用 实践案例 修改清单文件 打印 debug 级别的日志 方式一:直接代理 Lo ...

  7. 示例:WPF中Slider控件封装的缓冲播放进度条控件

    原文:示例:WPF中Slider控件封装的缓冲播放进度条控件 一.目的:模仿播放器播放进度条,支持缓冲任务功能 二.进度: 实现类似播放器中带缓存的播放样式(播放区域.缓冲区域.全部区域等样式) 实现 ...

  8. C# 创建json传输格式的http请求

    public static string PostRequestTest(string content, string url, string contentType = "applicat ...

  9. python基础03day

    # 1. # 创建字符串变量的三种写法及其区别 # 代码: #‘’.“”.“““””” # 区别: # 2. # 简述,计算机编程语言的分类及特点 # 1.机器 # 2.汇编 # 3.高级 # 3.1 ...

  10. vue项目的构建过程

    确保已经安装了node和npm 1.安装vue-cli npm i vue-cli -g 2.安装vue-router npm i vue-router --save 3.安装vue-router n ...