UOJ #228 - 基础数据结构练习题(势能线段树+复杂度分析)
神仙题。
乍一看和经典题 花神游历各国有一点像,只不过多了一个区间加操作。不过多了这个区间加操作就无法再像花神游历各国那样暴力开根直到最小值为 \(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 - 基础数据结构练习题(势能线段树+复杂度分析)的更多相关文章
- uoj#228. 基础数据结构练习题(线段树)
传送门 只有区间加区间开方我都会--然而加在一起我就gg了-- 然后这题的做法就是对于区间加直接打标记,对于区间开方,如果这个区间的最大值等于最小值就直接区间覆盖(据ljh_2000大佬说这个区间覆盖 ...
- 【线段树】uoj#228. 基础数据结构练习题
get到了标记永久化 sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧. 在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手.于是她的好朋友九条可怜酱给她出了一道题. 给出一 ...
- 【UOJ228】基础数据结构练习题(线段树)
[UOJ228]基础数据结构练习题(线段树) 题面 UOJ 题解 我们来看看怎么开根? 如果区间所有值都相等怎么办? 显然可以直接开根 如果\(max-sqrt(max)=min-sqrt(min)\ ...
- uoj #228. 基础数据结构练习题 线段树
#228. 基础数据结构练习题 统计 描述 提交 自定义测试 sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧. 在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手.于是她的 ...
- uoj#228 基础数据结构练习题
题面:http://uoj.ac/problem/228 正解:线段树. 我们可以发现,开根号时一个区间中的数总是趋近相等.判断一个区间的数是否相等,只要判断最大值和最小值是否相等就行了.如果这个区间 ...
- uoj#228. 基础数据结构练习题(线段树区间开方)
题目链接:http://uoj.ac/problem/228 代码:(先开个坑在这个地方) #include<bits/stdc++.h> using namespace std; ; l ...
- UOJ #228. 基础数据结构练习题 线段树 + 均摊分析 + 神题
题目链接 一个数被开方 #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",st ...
- HDU6315 Naive Operations(线段树 复杂度分析)
题意 题目链接 Sol 这题关键是注意到题目中的\(b\)是个排列 那么最终的答案最多是\(nlogn\)(调和级数) 设\(d_i\)表示\(i\)号节点还需要加\(d_i\)次才能产生\(1\)的 ...
- uoj#119. 【UR #8】决战圆锥曲线(线段树+复杂度分析)
题解 传送门 题解 然而要我来说我感觉只是个爆搜啊-- //minamoto #include<bits/stdc++.h> #define R register #define ll l ...
随机推荐
- python的虚拟环境Anaconda使用
Anaconda 使用conda常用命令 1.首先在所在系统中安装Anaconda.可以打开命令行输入conda -V检验是否安装以及当前conda的版本. 2.conda常用的命令. 1)con ...
- py3.8安装
ubantu python3.8# 命令下载wget https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tar.xz#解压tar -xvJf P ...
- Scrum Meeting 0427
零.说明 日期:2021-4-27 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成部分登录,注 ...
- Scrum Meeting 0529
零.说明 日期:2021-5-29 任务:简要汇报七日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 七日内已完成的任务 后两日计划完成的任务 困难 qsy PM&前端 完成后端管 ...
- TVS管性能及选型总结
https://wenku.baidu.com/view/5b5bda5526fff705cc170af8.html
- 转:BeanFactory和FactoryBean的区别
一.BeanFactory简介 BeanFacotry是spring中比较原始的Factory.如XMLBeanFactory就是一种典型的BeanFactory.原始的BeanFactory无法支持 ...
- 重学STM32---(九)之CAN通信(一)
目录 1.CAN 是什么 2.CAN 特点 3.错误状态的种类 4.总线拓扑 5.CAN 协议 1.CAN 是什么 CAN 是 Controller Area Network的缩写(以下称为 CA ...
- python教程-(四)当索引行不通时(python字典)
一.创建和使用字典 1.函数dict #字典表示方式如下 >>> phonebook = {'tom':'18616271234',"Jim":"186 ...
- hash 哈希表 缓存表
系统初始hash表为空,当外部命令执行时,默认会从 PATH路径下寻找该命令,找到后会将这条命令的路径记录到 hash表中,当再次使用该命令时,shell解释器首先会查看hash 表,存在将执行之,如 ...
- prometheus(4)之alertmanager报警插件
报警处理流程如下: 1. Prometheus Server监控目标主机上暴露的http接口(这里假设接口A),通过Promethes配置的'scrape_interval'定义的时间间隔,定期采集目 ...