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

Posted DaD3zZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UOJ#228基础数据结构练习题 线段树相关的知识,希望对你有一定的参考价值。

#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=0,f=1; char ch=getchar();
    while (ch<0 || ch>9) {if (ch==-) f=-1; ch=getchar();}
    while (ch>=0 && ch<=9) {x=x*10+ch-0; 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<<2];
    #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=0;
        tree[now].minx=tree[now].maxx=D;
        tree[now].sum=D*(tree[now].r-tree[now].l+1);
    }
    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+1)*D;
    }
    inline void PushDown(int now)
    {
        if (tree[now].l==tree[now].r) return;
        if (tree[now].cov!=-1)
            cover(ls,tree[now].cov),cover(rs,tree[now].cov),tree[now].cov=-1;        
        if (tree[now].tag!=0)
            modify(ls,tree[now].tag),modify(rs,tree[now].tag),tree[now].tag=0;
    }
    inline void BuildTree(int now,int l,int r)
    {
        tree[now].l=l; tree[now].r=r; tree[now].cov=-1;
        if (l==r) {tree[now].sum=tree[now].maxx=tree[now].minx=a[l]; return;} 
        int mid=(l+r)>>1;
        BuildTree(ls,l,mid); BuildTree(rs,mid+1,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)>>1;
        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+1) 
                    {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)>>1;
        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)>>1; LL re=0;
        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=1; i<=N; i++) a[i]=read();
    SegmentTree::BuildTree(1,1,N);
    while (M--)
        {
            int opt=read(),l=read(),r=read(),D;
            switch (opt)
                {
                    case 1: D=read(),SegmentTree::Modify(1,l,r,D); break;
                    case 2: SegmentTree::Change(1,l,r); break;
                    case 3: printf("%lld\n",SegmentTree::Query(1,l,r)); break;
                }
//            for (int i=1; i<=N; i++) printf("%d  ",SegmentTree::Query(1,i,i)); puts("=================");
        }
    return 0;
}

 

以上是关于UOJ#228基础数据结构练习题 线段树的主要内容,如果未能解决你的问题,请参考以下文章

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

UOJ228 基础数据结构练习题

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

uoj228 基础数据结构练习题

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

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