题面传送门

神仙题。

乍一看和经典题 花神游历各国有一点像,只不过多了一个区间加操作。不过多了这个区间加操作就无法再像花神游历各国那样暴力开根直到最小值为 \(1\) 为止的做法了,稍微感性理解一下即可明白,比方说你对一个已经全变为 \(1\) 的长度为 \(10^5\) 区间再加上 \(10^5\),那还需要 \(10^5\times\log 10^5\) 次区间修改操作才能变回原样,这样一来复杂度必然爆炸。

注意到我们对一个区间进行全局加,是不影响它的极差的,因此我们考虑用极差的角度思考这个问题,对于一段区间我们记录它的最大值和最小值,如果它们相等,即极差为 \(0\),那么显然区间开根就可以归约到区间加,直接打个标记即可。否则我们就继续递归左右区间进行修改。

为什么这样复杂度就对了呢?我们考虑记一个区间的为这个区间中元素的极差,不难发现我们对一个区间进行开根后容也会开根,因为假设区间最大值为 \(a\),最小值为 \(b\),那么本来容为 \(a-b\),开根后容为 \(\sqrt{a}-\sqrt{b}\),而 \(a-b=(\sqrt{a}-\sqrt{b})(\sqrt{a}+\sqrt{b})\),故 \(\dfrac{a-b}{\sqrt{a}-\sqrt{b}}>\sqrt{a}-\sqrt{b}\),证毕。也就是说我们对一个区间最多进行 \(\log n\) 次操作即可让它的容变为 \(0\)。

接下来考虑修改操作,由于我们要修改的区间要拆分成 \(\log n\) 个区间,而如果我们对一个区间进行全局加,是不影响它的极差的,因此一次修改最多影响 \(\log n\) 个区间的容,而就算我们将这 \(\log n\) 的区间的容都变得很大,打个比方,都变成 \(10^{10}\),那最多还是只需要 \(\log 10^{10}\times\log n\) 次操作才能将这些区间的容又变回 \(0\),因此每次区间带来的操作次数的影响是 \(\log n\log A\) 的,其中 \(A\) 为值域,因此总复杂度上界为 \(n\log A+m\log n\log A\),且异常跑不满。

但由于下取整可能会带来的误差,直接这样写是无法通过的,比方说序列 \(3\ 4\ 3\ 4\),开根后得到 \(1\ 2\ 1\ 2\),极差不变。因此我们可以构造出这样的 hack 数据:

100000 100000
255 256 255 256 ... 255 256
2 1 100000
2 1 100000
2 1 100000
3 1 100000
1 1 100000 255
2 1 100000
2 1 100000
2 1 100000
3 1 100000
1 1 100000 255
...
2 1 100000
2 1 100000
2 1 100000
3 1 100000
1 1 100000 255

出现这样的情况就 GG 了。

不过不难发现这种情况会发生当且仅当整段序列极差为 \(1\),并且开根后极差仍为 \(1\),这种情况还是可以规约为区间减的形式,稍微特判一下即可。

u1s1 这种题就是说起来好像很简单的亚子,但思考过程还是挺值得玩味的

const int MAXN=1e5;
int n,qu,a[MAXN+5];
struct node{int l,r;ll mx,mn,sum,lz;} s[MAXN*4+5];
void pushup(int k){
s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);
s[k].sum=s[k<<1].sum+s[k<<1|1].sum;
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r){s[k].mn=s[k].mx=s[k].sum=a[l];return;}
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void pushdown(int k){
if(s[k].lz){
s[k<<1].mn+=s[k].lz;s[k<<1].mx+=s[k].lz;s[k<<1].lz+=s[k].lz;
s[k<<1].sum+=s[k].lz*(s[k<<1].r-s[k<<1].l+1);
s[k<<1|1].mn+=s[k].lz;s[k<<1|1].mx+=s[k].lz;s[k<<1|1].lz+=s[k].lz;
s[k<<1|1].sum+=s[k].lz*(s[k<<1|1].r-s[k<<1|1].l+1);
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r) return s[k].mn+=x,s[k].mx+=x,s[k].sum+=1ll*(r-l+1)*x,s[k].lz+=x,void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
pushup(k);
}
ll query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].sum;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
}
void sqrt(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r){
ll mnv=(ll)sqrt(s[k].mn),mxv=(ll)sqrt(s[k].mx);
if(s[k].mn==s[k].mx){
ll dif=mnv-s[k].mn;
s[k].mn+=dif;s[k].mx+=dif;s[k].sum+=dif*(r-l+1);s[k].lz+=dif;
} else if(s[k].mx-s[k].mn==1&&mxv-mnv==1){
ll dif=mnv-s[k].mn;
s[k].mn+=dif;s[k].mx+=dif;s[k].sum+=dif*(r-l+1);s[k].lz+=dif;
} else {
int mid=l+r>>1;pushdown(k);
sqrt(k<<1,l,mid);sqrt(k<<1|1,mid+1,r);
pushup(k);
} return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) sqrt(k<<1,l,r);
else if(l>mid) sqrt(k<<1|1,l,r);
else sqrt(k<<1,l,mid),sqrt(k<<1|1,mid+1,r);
pushup(k);
}
int main(){
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
while(qu--){
int opt,l,r,x;scanf("%d",&opt);
switch(opt){
case 1:{scanf("%d%d%d",&l,&r,&x);modify(1,l,r,x);break;}
case 2:{scanf("%d%d",&l,&r);sqrt(1,l,r);break;}
case 3:{scanf("%d%d",&l,&r);printf("%lld\n",query(1,l,r));break;}
}
}
return 0;
}

UOJ #228 - 基础数据结构练习题(势能线段树+复杂度分析)的更多相关文章

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

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

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

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

  3. 【UOJ228】基础数据结构练习题(线段树)

    [UOJ228]基础数据结构练习题(线段树) 题面 UOJ 题解 我们来看看怎么开根? 如果区间所有值都相等怎么办? 显然可以直接开根 如果\(max-sqrt(max)=min-sqrt(min)\ ...

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

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

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

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

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

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

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

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

  8. HDU6315 Naive Operations(线段树 复杂度分析)

    题意 题目链接 Sol 这题关键是注意到题目中的\(b\)是个排列 那么最终的答案最多是\(nlogn\)(调和级数) 设\(d_i\)表示\(i\)号节点还需要加\(d_i\)次才能产生\(1\)的 ...

  9. uoj#119. 【UR #8】决战圆锥曲线(线段树+复杂度分析)

    题解 传送门 题解 然而要我来说我感觉只是个爆搜啊-- //minamoto #include<bits/stdc++.h> #define R register #define ll l ...

随机推荐

  1. 初学python-day5 流程控制

    流程控制 一.判断语句 1.if语句(单一条件分支) 结构: if    表达式: 为真的时候执行的语句 概述:当程序运行到if语句时候,首页要计算表达式的值,判断真假,如果表达式的职位为真,则执行i ...

  2. Apache Zookeeper Java客户端Curator使用及权限模式详解

    这篇文章是让大家了解Zookeeper基于Java客户端Curator的基本操作,以及如何使用Zookeeper解决实际问题. Zookeeper基于Java访问 针对zookeeper,比较常用的J ...

  3. Map中getOrDefault()与数值进行比较

    一般用哈希表计数时,value类型通常为Integer.如果想比较某个key出现的次数,使用get(key)与某个数值进行比较是有问题的.当哈希表中并不包含该key时,因为此时get方法返回值是nul ...

  4. 第五课第四周笔记1:Transformer Network Intuition 变压器网络直觉

    目录 Transformer Network Intuition 变压器网络直觉 Transformer Network Intuition 变压器网络直觉 深度学习中最令人兴奋的发展之一是 Tran ...

  5. BUAA_2020_软件工程_热身作业

    项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 热身作业要求 我在这个课程的目标 了解软件工程的技术,掌握工程化开发的能力 这个作业在哪个具体方面 ...

  6. 8M的摄像头,30fps摄像时,60hz的LCD刷新频率,请问camera每秒向BB传输多少数据,如何计算

    8M的摄像头,30fps摄像时,60hz的LCD刷新频率,请问camera每秒向BB传输多少数据,如何计算 xiang2012 Post at 2012/8/7 10:37:33 8M的摄像头,30f ...

  7. 好的编程习惯是减少bug最有效的方法

    公司来了几个新手,有时候很简单的一个功能模块都要耗费好几天时间,总是在一些不相关的问题上死耗一整天,搞出莫名其妙的问题,找不到具体原因,总是怀疑编译出问题了,系统出问题了,板子出问题了,搞到快下班了叫 ...

  8. Qt信号与槽传递自定义数据类型——两种解决方法

    信号与槽作为qt中的核心机制,在qt应用开发中经常会用的,但是原生的信号与槽连接传参,只支持基本的数据类型,比如char,int, float,double. 如果想要在信号与槽之间传递自定义参数,比 ...

  9. poj 3020 Antenna Placement(二分图最大匹配)

    题意: N行M列的矩阵,每个格子里不是 * 就是 O . * :是一个利益点. O:是一个空白点. 每次可以用一个圈覆盖相邻的两个*.(左右相邻或上下相邻). 问最少需要多少个圈可以覆盖所有的*. 思 ...

  10. Swoft+Docker

    Docker 以下纯属个人理解: Docker就是一种虚拟机,将环境打包成镜像,等于做了一个Linux系统裁剪. 镜像就是我们安装系统的镜像,里面包含了你的代码和环境. 容器就是一个虚拟机,你可以用一 ...