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

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

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 400005;

ll sum[N],mx[N],mn[N],tag[N],src[N];

void add(ll p,ll l,ll r,ll v)
{
    sum[p]+=v*(r-l+1);
    mx[p]+=v;
    mn[p]+=v;
    tag[p]+=v;
}

void pushup(int p)
{
    sum[p]=sum[p*2]+sum[p*2+1];
    mx[p]=max(mx[p*2],mx[p*2+1]);
    mn[p]=min(mn[p*2],mn[p*2+1]);
}

void pushdown(int p,int l,int r)
{
    if(tag[p])
    {
        add(p*2,l,(l+r)/2,tag[p]);
        add(p*2+1,(l+r)/2+1,r,tag[p]);
        tag[p]=0;
    }
}

void build(int p,int l,int r)
{
    if(l==r)
    {
        sum[p]=mx[p]=mn[p]=src[l];
    }
    else
    {
        build(p*2,l,(l+r)/2);
        build(p*2+1,(l+r)/2+1,r);
        pushup(p);
    }
}

void modify(int p,int l,int r,int ql,int qr,int v)
{
    if(l>qr||r<ql) return;
    if(l>=ql&&r<=qr)
    {
        add(p,l,r,v);
    }
    else
    {
        pushdown(p,l,r);
        modify(p*2,l,(l+r)/2,ql,qr,v);
        modify(p*2+1,(l+r)/2+1,r,ql,qr,v);
        pushup(p);
    }
}

void change(int p,int l,int r,int ql,int qr)
{
    if(l>qr||r<ql) return;
    if(l>=ql&&r<=qr && mx[p]-(ll)sqrt(mx[p]) == mn[p]-(ll)sqrt(mn[p]))
    {
        add(p,l,r,(ll)sqrt(mx[p])-mx[p]);
    }
    else
    {
        pushdown(p,l,r);
        change(p*2,l,(l+r)/2,ql,qr);
        change(p*2+1,(l+r)/2+1,r,ql,qr);
        pushup(p);
    }
}

ll query(int p,int l,int r,int ql,int qr)
{
    if(l>qr||r<ql) return 0;
    if(l>=ql&&r<=qr)
    {
        return sum[p];
    }
    else
    {
        pushdown(p,l,r);
        return query(p*2,l,(l+r)/2,ql,qr) + query(p*2+1,(l+r)/2+1,r,ql,qr);
    }
}

int main()
{
    ios::sync_with_stdio(false);
    int n,m,t1,t2,t3,t4;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>src[i];
    }
    build(1,1,n);

    for(int i=1;i<=m;i++)
    {
        cin>>t1>>t2>>t3;
        if(t1==1)
        {
            cin>>t4;
            modify(1,1,n,t2,t3,t4);
        }
        if(t1==2)
        {
            change(1,1,n,t2,t3);
        }
        if(t1==3)
        {
            cout<<query(1,1,n,t2,t3)<<endl;
        }
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/11675469.html

时间: 2024-10-11 17:39:43

[UOJ228] 基础数据结构练习题 - 线段树的相关文章

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

#228. 基础数据结构练习题 题目链接:http://uoj.ac/problem/228 Solution 这题由于有区间+操作,所以和花神还是不一样的. 花神那道题,我们可以考虑每个数最多开根几次就会成1,而这个必须利用开根的性质 我们维护区间最大.最小.和.区间加操作可以直接做. 区间开方操作需要特殊考虑. 首先对于一个区间,如果这个区间的所有数取$x=\left \lfloor \sqrt{x} \right \rfloor$值一样,那么就可以直接区间覆盖. 分析上述过程,一个区间可以

UOJ228 基础数据结构练习题

本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! 题目链接:http://uoj.ac/problem/228 本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 正解:线段树 解题报告: 这道题是一道线段树的神题,需要我们支持区间开方.区间加法和区间求和操作. 关键是对于开方操作的处理,考虑如果一个区间最大值等于最小值(即全都相等),那么就可以直接开方,

《ACM/ICPC 算法训练教程》读书笔记 之 数据结构(线段树详解)

依然延续第一篇读书笔记,这一篇是基于<ACM/ICPC 算法训练教程>上关于线段树的讲解的总结和修改(这本书在线段树这里Error非常多),但是总体来说这本书关于具体算法的讲解和案例都是不错的. 线段树简介 这是一种二叉搜索树,类似于区间树,是一种描述线段的树形数据结构,也是ACMer必学的一种数据结构,主要用于查询对一段数据的处理和存储查询,对时间度的优化也是较为明显的,优化后的时间复杂为O(logN).此外,线段树还可以拓展为点树,ZWK线段树等等,与此类似的还有树状数组等等. 例如:要将

《数据结构》线段树入门(二)

今天继续介绍——线段树之延迟标记 接上期<数据结构>线段树入门(一):http://www.cnblogs.com/shadowland/p/5870339.html 在上期介绍了线段树的最基本内容(线段树单点修改,区间查询),这次将介绍:区间修改,区间查询. Question: 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述: 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一数是1,后接3个正

【uoj228】 基础数据结构练习题

http://uoj.ac/problem/228 (题目链接) 题意 给出一个序列,维护区间加法,区间开根,区间求和 Solution 线段树.考虑区间开根怎么做.当区间的最大值与最小值相等时,我们直接对整个区间开根.最坏情况下,一次开根的复杂度最坏是${O(nlogn)}$的,然而每次开根可以迅速拉近两个数之间的大小差距,最坏复杂度的开根不会超过${5}$次. 但是考虑这样一种情况:${\sqrt{x+1}=\sqrt{x}+1}$,如果序列长成这样:${65535,65536,65535,

《数据结构》线段树入门(一)

今天介绍一种非常特殊的数据结构——线段树 首先提出一个问题: 给你n个数,有两种操作: 1:给第i个数的值增加X 2:询问区间[a,b]的总和是什么? 输入描述 输入文件第一行为一个整数n,接下来是n行n个整数,表示格子中原来的整数.接下一个正整数q,再接 下来有q行,表示q个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给 位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和. 样例输入 4 7 6 3 5 2 1 1

数据结构2——线段树

一.相关介绍 线段树:它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题.由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn). 线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]. 下面我们从一个经典的例子来了解线段树,问题描述如下: 从数组arr[0...n-1]中查找某个(子)数组内的最小值,其中数组大小固定,但是数组中

【数据结构】线段树 (定义 &amp; 点修改/区间查询)

[本文描述高级数据结构线段树的定义] [并解决 点修改/区间查询 的问题] 结构与定义 线段树的基本结构 ? 由图可知,线段树的每一个节点都代表着一段区间 且同一层的节点(深度相同的节点)所表示的区间互不重叠 所有叶子节点代表的区间左边界与右边界相同(叶子节点代表单个元素) 普遍规定 如果某个非叶子节点代表的区间包含元素个数为奇数 则它的左儿子包含的元素个数比右儿子大 1 在代码部分,非叶子节点表示区间为 [l,r] 则左儿子为 [ l , (l+r)/2 ] ,右儿子为 [ (l+r)/2+1

数据结构之线段树

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]. 因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 用线段树解题关键是要清楚的知道每个节点要存放什么信息,以及这些节点如何高效的更新,维护,查询,不一定每次都更新到叶子节点,这样复杂度最坏有可能是0(n) 下面附上一道题 poj3264