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

题目链接:http://uoj.ac/problem/228

Solution

这题由于有区间+操作,所以和花神还是不一样的。 花神那道题,我们可以考虑每个数最多开根几次就会成1,而这个必须利用开根的性质

我们维护区间最大、最小、和。区间加操作可以直接做。

区间开方操作需要特殊考虑。

首先对于一个区间,如果这个区间的所有数取$x=\left \lfloor \sqrt{x} \right \rfloor$值一样,那么就可以直接区间覆盖。

分析上述过程,一个区间可以直接覆盖,当这个区间的差值满足一个特定的范围。 而每次开方这个差值就会减少,可以证明这样开方$lg^{2}$次就会全部为1

所以剩下的我们就可递归下去。

这样的话,区间+操作,就相当于重置了这个差值,所以复杂度还是科学的。

但是有一种情况出现问题。

上述是每次开方后,差值减小,但是有开方后差值不变的情况。  例如 3 4 3 4 3 4 3 4

即$a$,$b$当$b$为完全平方数,$a=b-1$时。这样开方完差值还是1,然后区间+2就又变回来了。 这样上述就卡成了暴力。

那么我们把这种情况特殊考虑。 这样可以转化为一个区间-的操作。剩下的暴力递归,这样就可以了。

时间复杂度是$O(NlogNlg^2{N})$

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
inline int read()
{
int x=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
#define MAXN 100010
int N,M,a[MAXN];
namespace SegmentTree
{
struct SegmentTreeNode{int l,r,cov; LL tag,sum,maxx,minx;}tree[MAXN<<];
#define ls now<<1
#define rs now<<1|1
inline void Update(int now)
{
tree[now].sum=tree[ls].sum+tree[rs].sum;
tree[now].maxx=max(tree[ls].maxx,tree[rs].maxx);
tree[now].minx=min(tree[ls].minx,tree[rs].minx);
}
inline void cover(int now,int D)
{
tree[now].cov=D; tree[now].tag=;
tree[now].minx=tree[now].maxx=D;
tree[now].sum=D*(tree[now].r-tree[now].l+);
}
inline void modify(int now,LL D)
{
tree[now].tag+=D;
tree[now].minx+=D; tree[now].maxx+=D; tree[now].sum+=(tree[now].r-tree[now].l+)*D;
}
inline void PushDown(int now)
{
if (tree[now].l==tree[now].r) return;
if (tree[now].cov!=-)
cover(ls,tree[now].cov),cover(rs,tree[now].cov),tree[now].cov=-;
if (tree[now].tag!=)
modify(ls,tree[now].tag),modify(rs,tree[now].tag),tree[now].tag=;
}
inline void BuildTree(int now,int l,int r)
{
tree[now].l=l; tree[now].r=r; tree[now].cov=-;
if (l==r) {tree[now].sum=tree[now].maxx=tree[now].minx=a[l]; return;}
int mid=(l+r)>>;
BuildTree(ls,l,mid); BuildTree(rs,mid+,r);
Update(now);
}
inline void Modify(int now,int L,int R,int D)
{
int l=tree[now].l,r=tree[now].r;
PushDown(now);
if (L<=l && R>=r) {modify(now,D); return;}
int mid=(l+r)>>;
if (L<=mid) Modify(ls,L,R,D);
if (R>mid) Modify(rs,L,R,D);
Update(now);
}
inline void Change(int now,int L,int R)
{
int l=tree[now].l,r=tree[now].r;
PushDown(now);
if (L<=l && R>=r)
{
if ((int)sqrt(tree[now].maxx)==(int)sqrt(tree[now].minx))
{cover(now,(int)sqrt(tree[now].maxx)); return;}
if (tree[now].maxx==tree[now].minx+)
{modify(now,(int)sqrt(tree[now].minx)-tree[now].minx); return;}
if (l!=r) Change(ls,L,R),Change(rs,L,R);
Update(now);
return;
}
int mid=(l+r)>>;
if (L<=mid) Change(ls,L,R);
if (R>mid) Change(rs,L,R);
Update(now);
}
inline LL Query(int now,int L,int R)
{
int l=tree[now].l,r=tree[now].r;
PushDown(now);
if (L<=l && R>=r) return tree[now].sum;
int mid=(l+r)>>; LL re=;
if (L<=mid) re+=Query(ls,L,R);
if (R>mid) re+=Query(rs,L,R);
return re;
}
}
int main()
{
N=read(),M=read();
for (int i=; i<=N; i++) a[i]=read();
SegmentTree::BuildTree(,,N);
while (M--)
{
int opt=read(),l=read(),r=read(),D;
switch (opt)
{
case : D=read(),SegmentTree::Modify(,l,r,D); break;
case : SegmentTree::Change(,l,r); break;
case : printf("%lld\n",SegmentTree::Query(,l,r)); break;
}
// for (int i=1; i<=N; i++) printf("%d ",SegmentTree::Query(1,i,i)); puts("=================");
}
return ;
}

【UOJ#228】基础数据结构练习题 线段树的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. 【uoj#228】基础数据结构练习题 线段树+均摊分析

    题目描述 给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有三种:区间加.区间开根.区间求和. $n,m,a_i\le 100000$ . 题解 线段树+均摊分析 对于原来的两个数 $a$ ...

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

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

  8. UOJ #228 - 基础数据结构练习题(势能线段树+复杂度分析)

    题面传送门 神仙题. 乍一看和经典题 花神游历各国有一点像,只不过多了一个区间加操作.不过多了这个区间加操作就无法再像花神游历各国那样暴力开根直到最小值为 \(1\) 为止的做法了,稍微感性理解一下即 ...

  9. [UOJ228] 基础数据结构练习题 - 线段树

    考虑到一个数开根号 \(loglog\) 次后就会变成1,设某个Node的势能为 \(loglog(maxv-minv)\) ,那么一次根号操作会使得势能下降 \(1\) ,一次加操作最多增加 \(l ...

随机推荐

  1. 在 CentOS7 上安装 MySQL5.7

    在 CentOS7 上安装 MySQL5.7 1 通过 SecureCRT 连接到阿里云 CentOS7 服务器: 2 进入到目录 /usr/local/ 中: cd /usr/local/ 3 创建 ...

  2. C语言递归,非递归实现翻转链表

    翻转链表作为,链表的常用操作,也是面试常遇到的. 分析非递归分析: 非递归用的小技巧比较多,很容易出错. 递归分析比较简单,在代码里面 代码: #include<stdio.h> #inc ...

  3. ADO.NET五大对象理论和实践(草稿)

    一.ADO.NET五大对象理论 1. Connection:与数据源建立连接. 2. Command:对数据源执行SQL命令并返回结果. Command对象在执行的的时候有几个比较重要的方法,如Exe ...

  4. DBCC CheckDB遇到a database snapshot could not be created

    在备份一个客户的数据库时(数据库版本为SQL 2005 Express版本),做DBCC CHECKDB时遇到了下面错误信息: dbcc checkdb('DB_NAME'); 消息 5030,级别 ...

  5. linux快速删除海量文件

    首先创建一个临时目录: /rubbish rsync --delete-before -a -H -v --progress --stats /rubbish /111 /111为目的目录 选项说明: ...

  6. x01.Weiqi.10: 死活问题

    估计得不错,点目后,仅一个方法:UpdateMeshes5() 就完美解决了梅花六.刀把五.斗笠四.盘角曲四等死活问题.先来看看效果图: 其代码如下: void UpdateMeshes5(bool ...

  7. 解决开启SQL Server sql Always on Group 事务日志增大的问题

    配置了Alwayson之后,因为没有只能使用完全恢复模式,不能使用简单或大容量日志模式,所以日志不断增长,不能使用改变恢复模式的方式清空日志 手动操作收缩或截断日志也无效 读了一些文章后发现,有人使用 ...

  8. C#学习笔记-KeyDown、KeyPress、KeyUp事件以及KeyCode、KeyData、KeyValue、KeyChar属性

    本来没打算单独写的,但是在自己弄测试小程序的时候,越写发现不清楚的东西越多,所以实践又一次证明:纸上得来终觉浅,绝知此事要躬行! 直接贴代码了: //发生顺序:KeyDown->KeyPress ...

  9. 关于Hadoop的集群环境下虚拟机采用NAT方式连不上网的解决

    使用虚拟机搭建hadoop集群的时候采用的是NAT方式联网,但是会出现时常掉网的现象,查看后排查了很久也没有发现什么问题. 很可能是Windows下的NAT服务没有开启,这时候尽管虚拟机和主机是在一个 ...

  10. [AR]高通Vuforia之Frame Markers

    软件环境 SDK:FrameMarkers-6-0-112.unitypackage(从官网 -> Download -> Samples -> Core Features 下载 ) ...