题面传送门

神仙题。

乍一看和经典题 花神游历各国有一点像,只不过多了一个区间加操作。不过多了这个区间加操作就无法再像花神游历各国那样暴力开根直到最小值为 \(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. PHP伪协议与文件包含漏洞1

    PHP文件包含漏洞花样繁多,需配合代码审计. 看能否使用这类漏洞时,主要看: (1)代码中是否有include(),且参数可控: 如: (2)php.ini设置:确保 allow_url_fopen= ...

  2. 什么是产品待办列表?(What is Product Backlog)

    正如scrum指南中所描述的,产品待办事项列表是一个紧急而有序的列表,其中列出了改进产品所需的内容.它是scrum团队承担的工作的唯一来源. 在sprint计划 (Sprint Planning)活动 ...

  3. BUAA SE 软件案例分析-CSDN

    Q A 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业-软件案例分析 我在这个课程的目标是 系统地学习软件工程开发知识,掌握相关流程和技术,提升 ...

  4. 主集天线和分集天线——4G天线技术

    主集天线和分集天线 分集接收技术是一项主要的抗衰落技术,可以大大提高多径衰落信道传输下的可靠性,在实际的移动通信系统中,移动台常常工作在城市建筑群或其他复杂的地理环境中,而且移动的速度和方向是任意的. ...

  5. 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(一)

    一.算法介绍 迪杰斯特拉算法(英语:Dijkstra's algorithm)由荷兰计算机科学家艾兹赫尔·迪杰斯特拉在1956年提出.迪杰斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题. ...

  6. NavigationView使用简介

    Android支持直接创建带有NavigationView的Activity,这里主要介绍NavigationView的逻辑. NavigationView通常是跟DrawerLayout一起使用.D ...

  7. 手把手搭建自己的智能家居 - 基于 IOT Pi 的智能甲醛检测器

    智慧家居 - 基于 IOT Pi 的智能甲醛检测器 之前的文章体验 MS-RTOS 的时候入手了一个块 IOT Pi ,放着也是浪费,这次我们就利用 IOT PI 开发一个智能甲醛检测器.φ(> ...

  8. amba web

    arm amba doc https://developer.arm.com/docs

  9. 碰撞的蚂蚁 牛客网 程序员面试金典 C++ Java Python

    碰撞的蚂蚁 牛客网 程序员面试金典 C++ Java Python 题目描述 在n个顶点的多边形上有n只蚂蚁,这些蚂蚁同时开始沿着多边形的边爬行,请求出这些蚂蚁相撞的概率.(这里的相撞是指存在任意两只 ...

  10. hdu 1159 Common Subsequence(最长公共子序列,DP)

    题意: 两个字符串,判断最长公共子序列的长度. 思路: 直接看代码,,注意边界处理 代码: char s1[505], s2[505]; int dp[505][505]; int main(){ w ...