题目描述

给出一个长度为 $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)$ 。

#include <cmath>
#include <cstdio>
#include <algorithm>
#define N 100010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef long long ll;
ll sum[N << 2] , mx[N << 2] , mn[N << 2] , tag[N << 2];
inline void add(ll v , int l , int r , int x)
{
sum[x] += v * (r - l + 1) , mx[x] += v , mn[x] += v , tag[x] += v;
}
inline void pushup(int x)
{
sum[x] = sum[x << 1] + sum[x << 1 | 1];
mx[x] = max(mx[x << 1] , mx[x << 1 | 1]);
mn[x] = min(mn[x << 1] , mn[x << 1 | 1]);
}
inline void pushdown(int l , int r , int x)
{
if(tag[x])
{
int mid = (l + r) >> 1;
add(tag[x] , lson) , add(tag[x] , rson);
tag[x] = 0;
}
}
inline void build(int l , int r , int x)
{
if(l == r)
{
scanf("%lld" , &sum[x]) , mx[x] = mn[x] = sum[x];
return;
}
int mid = (l + r) >> 1;
build(lson) , build(rson);
pushup(x);
}
inline void update(int b , int e , ll a , int l , int r , int x)
{
if(b <= l && r <= e)
{
add(a , l , r , x);
return;
}
pushdown(l , r , x);
int mid = (l + r) >> 1;
if(b <= mid) update(b , e , a , lson);
if(e > mid) update(b , e , a , rson);
pushup(x);
}
inline void change(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e && mx[x] - (ll)sqrt(mx[x]) == mn[x] - (ll)sqrt(mn[x]))
{
add((ll)sqrt(mx[x]) - mx[x] , l , r , x);
return;
}
pushdown(l , r , x);
int mid = (l + r) >> 1;
if(b <= mid) change(b , e , lson);
if(e > mid) change(b , e , rson);
pushup(x);
}
inline ll query(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return sum[x];
pushdown(l , r , x);
int mid = (l + r) >> 1;
ll ans = 0;
if(b <= mid) ans += query(b , e , lson);
if(e > mid) ans += query(b , e , rson);
return ans;
}
int main()
{
int n , m , opt , x , y;
ll z;
scanf("%d%d" , &n , &m);
build(1 , n , 1);
while(m -- )
{
scanf("%d%d%d" , &opt , &x , &y);
if(opt == 1) scanf("%lld" , &z) , update(x , y , z , 1 , n , 1);
else if(opt == 2) change(x , y , 1 , n , 1);
else printf("%lld\n" , query(x , y , 1 , n , 1));
}
return 0;
}

最新文章

  1. html5快速入门(二)—— CSS简介
  2. HDU 1003 动态规划
  3. 20145330孙文馨 《Java程序设计》第一周学习总结
  4. 自制html5塔防游戏
  5. js数组判断是否含有某一个元素
  6. 在.NET中实现彩色光标/动画光标和自定义光标[转]
  7. 软件测试 homework1
  8. 转 Android - 文件操作
  9. .net转php laraval框架学习系列(二)项目实战---Models
  10. [置顶] android 自定义TextView
  11. JTree
  12. 荣耀7.0系统手机最简单激活Xposed框架的步骤
  13. vue组件之间的传值方式
  14. vuex 、store、state (转载)
  15. MetaMask/metamask-extension/mascara 的运行实现
  16. python 全局变量的使用
  17. Flask 学习篇二:学习Flask过程中的记录
  18. Jmeter自动化测试 POST请求和GET请求用if控制器,可以二次开发源码,将请求方式通过数据源传入,就不需要做多余的判断
  19. Flash OS images to SD cards &amp; USB drives &amp; TF cards safely and easily using etcher
  20. SQLServer 的存储过程与java交互

热门文章

  1. SQL Server 日期格式和日期操作
  2. 办公区公网Ip访问不到阿里云ECS
  3. ncl 函数源码 gc_inout
  4. electron快速开始
  5. 第五章 if语句
  6. [Windows][C#][.NET][WPF]基于ArcFace2.0+红外双目摄像头的活体检测
  7. golang slice使用不慎导致的问题
  8. Java 内存模型_2
  9. nginx正向vs反向代理
  10. 【quickhybrid】组件(自定义)API的实现