题目描述

给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有三种:区间加、区间开根、区间求和。

$n,m,a_i\le 100000$ 。


题解

线段树+均摊分析

对于原来的两个数 $a$ 和 $b$ ( $a>b$ ) ,开根后变成 $\sqrt a$ 和 $\sqrt b$ ,它们的差从 $a-b$ 变成了 $\sqrt a-\sqrt b$ 。

又有 $(\sqrt a-\sqrt b)(\sqrt a+\sqrt b)=a-b$ ,因此开方后的差小于原来差的开方。

而当区间差为 $0$ 或 $a=x^2,b=x^2-1$ 的 $1$ 时,区间开根就变成了区间减。

因此一个区间开根 $\log\log(Max-Min)$ 次后就不需要暴力开根,直接区间减即可。

定义线段树节点势能为 $\log\log(Max-Min)$ ,那么每次对 $[l,r]$ 开根就是将所有 $l\le x,y\le r$ ,且势能不为 $0$ 的节点 $[x,y]$ 的势能减 $1$ ,代价为势能减少总量。

分析区间加操作:只会修改到经过的节点的势能,影响 $\log$ 个节点,将这些点的势能恢复为 $\log\log(Max-Min)$ 。

因此总的时间复杂度就是总势能量 $O((n+m\log n)\log\log a)$ 。

  1. #include <cmath>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #define N 100010
  5. #define lson l , mid , x << 1
  6. #define rson mid + 1 , r , x << 1 | 1
  7. using namespace std;
  8. typedef long long ll;
  9. ll sum[N << 2] , mx[N << 2] , mn[N << 2] , tag[N << 2];
  10. inline void add(ll v , int l , int r , int x)
  11. {
  12. sum[x] += v * (r - l + 1) , mx[x] += v , mn[x] += v , tag[x] += v;
  13. }
  14. inline void pushup(int x)
  15. {
  16. sum[x] = sum[x << 1] + sum[x << 1 | 1];
  17. mx[x] = max(mx[x << 1] , mx[x << 1 | 1]);
  18. mn[x] = min(mn[x << 1] , mn[x << 1 | 1]);
  19. }
  20. inline void pushdown(int l , int r , int x)
  21. {
  22. if(tag[x])
  23. {
  24. int mid = (l + r) >> 1;
  25. add(tag[x] , lson) , add(tag[x] , rson);
  26. tag[x] = 0;
  27. }
  28. }
  29. inline void build(int l , int r , int x)
  30. {
  31. if(l == r)
  32. {
  33. scanf("%lld" , &sum[x]) , mx[x] = mn[x] = sum[x];
  34. return;
  35. }
  36. int mid = (l + r) >> 1;
  37. build(lson) , build(rson);
  38. pushup(x);
  39. }
  40. inline void update(int b , int e , ll a , int l , int r , int x)
  41. {
  42. if(b <= l && r <= e)
  43. {
  44. add(a , l , r , x);
  45. return;
  46. }
  47. pushdown(l , r , x);
  48. int mid = (l + r) >> 1;
  49. if(b <= mid) update(b , e , a , lson);
  50. if(e > mid) update(b , e , a , rson);
  51. pushup(x);
  52. }
  53. inline void change(int b , int e , int l , int r , int x)
  54. {
  55. if(b <= l && r <= e && mx[x] - (ll)sqrt(mx[x]) == mn[x] - (ll)sqrt(mn[x]))
  56. {
  57. add((ll)sqrt(mx[x]) - mx[x] , l , r , x);
  58. return;
  59. }
  60. pushdown(l , r , x);
  61. int mid = (l + r) >> 1;
  62. if(b <= mid) change(b , e , lson);
  63. if(e > mid) change(b , e , rson);
  64. pushup(x);
  65. }
  66. inline ll query(int b , int e , int l , int r , int x)
  67. {
  68. if(b <= l && r <= e) return sum[x];
  69. pushdown(l , r , x);
  70. int mid = (l + r) >> 1;
  71. ll ans = 0;
  72. if(b <= mid) ans += query(b , e , lson);
  73. if(e > mid) ans += query(b , e , rson);
  74. return ans;
  75. }
  76. int main()
  77. {
  78. int n , m , opt , x , y;
  79. ll z;
  80. scanf("%d%d" , &n , &m);
  81. build(1 , n , 1);
  82. while(m -- )
  83. {
  84. scanf("%d%d%d" , &opt , &x , &y);
  85. if(opt == 1) scanf("%lld" , &z) , update(x , y , z , 1 , n , 1);
  86. else if(opt == 2) change(x , y , 1 , n , 1);
  87. else printf("%lld\n" , query(x , y , 1 , n , 1));
  88. }
  89. return 0;
  90. }

【uoj#228】基础数据结构练习题 线段树+均摊分析的更多相关文章

  1. UOJ #228. 基础数据结构练习题 线段树 + 均摊分析 + 神题

    题目链接 一个数被开方 #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",st ...

  2. uoj #228. 基础数据结构练习题 线段树

    #228. 基础数据结构练习题 统计 描述 提交 自定义测试 sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧. 在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手.于是她的 ...

  3. uoj#228. 基础数据结构练习题(线段树区间开方)

    题目链接:http://uoj.ac/problem/228 代码:(先开个坑在这个地方) #include<bits/stdc++.h> using namespace std; ; l ...

  4. 【loj6029】「雅礼集训 2017 Day1」市场 线段树+均摊分析

    题目描述 给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有四种:区间加.区间下取整除.区间求最小值.区间求和. $n\le 100000$ ,每次加的数在 $[-10^4,10^4]$ 之 ...

  5. 【线段树】uoj#228. 基础数据结构练习题

    get到了标记永久化 sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧. 在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手.于是她的好朋友九条可怜酱给她出了一道题. 给出一 ...

  6. 【UOJ#228】基础数据结构练习题 线段树

    #228. 基础数据结构练习题 题目链接:http://uoj.ac/problem/228 Solution 这题由于有区间+操作,所以和花神还是不一样的. 花神那道题,我们可以考虑每个数最多开根几 ...

  7. uoj#228 基础数据结构练习题

    题面:http://uoj.ac/problem/228 正解:线段树. 我们可以发现,开根号时一个区间中的数总是趋近相等.判断一个区间的数是否相等,只要判断最大值和最小值是否相等就行了.如果这个区间 ...

  8. bzoj4127 Abs 树链剖分+线段树+均摊分析

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4127 题解 首先区间绝对值和可以转化为 \(2\) 倍的区间正数和 \(-\) 区间和.于是问 ...

  9. uoj#228. 基础数据结构练习题(线段树)

    传送门 只有区间加区间开方我都会--然而加在一起我就gg了-- 然后这题的做法就是对于区间加直接打标记,对于区间开方,如果这个区间的最大值等于最小值就直接区间覆盖(据ljh_2000大佬说这个区间覆盖 ...

随机推荐

  1. UWP 五星评价(不跳转到龟速商店)

    之前写过一篇文章  UWP 五星好评  代码如下 var pfn = Package.Current.Id.FamilyName; await Launcher.LaunchUriAsync(new ...

  2. [Selenium]如何通过Selenium实现Ctrl+click,即按住Ctrl的同时进行单击操作

    [以下是不负责任的转载……] 在自动化测试的过程中,经常会出现这样的场景: 按住Ctrl的同时,进行单击操作,已达到多选的目的 Actions a = new Actions(driver); a.k ...

  3. Html.RenderPartial与Html.RenderAction的区别

    Html.RenderPartial与Html.RenderAction这两个方法都是用来在界面上嵌入用户控件的. Html.RenderPartial是直接将用户控件嵌入到界面上: <%Htm ...

  4. [Java] Design Pattern:Code Shape - manage your code shape

    [Java] Design Pattern:Code Shape - manage your code shape Code Shape Design Pattern Here I will intr ...

  5. Vue随性小笔记

    1 前端MVC 和 后端MVC不同: 可以看出前端MVC其实为了解决前端复杂js模块化的问题,从后端MVC的V分离出来的 2     MVC / MVP / MVVM 三者区别  Model View ...

  6. GitLab篇之备份还原

    1. GitLab备份配置 输入以下命令,打开gitlab配置文件 [root@code-server ~]# vim /etc/gitlab/gitlab.rb 修改以下配置,gitlab有自动清理 ...

  7. Ubuntu 16.04安装tensorflow_gpu的方法

    参考资料: Ubuntu 16.04安装tensorflow_gpu 1.9.0的方法 装Tensorflow,运行项目报错: module compiled against API version ...

  8. Spring学习(5):DI的配置

    一.  一些概念 应用程序中说的依赖一般指类之间的关系. 泛化:表示类与类之间的继承关系.接口与接口之间的继承关系: 实现:表示类对接口的实现: 依赖:当类与类之间有使用关系时就属于依赖关系,不同于关 ...

  9. Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

    Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...

  10. Ubuntu系统无法识别Logitech M590蓝牙鼠标的问题

    参见 - https://blog.csdn.net/yh2869/article/details/73119018 亲测可用. 系统:ubuntu 16.04 64bit 现象:鼠标配对可以成功,但 ...