UOJ 228 基础数据结构练习题 线段树区间开根+区间求和

http://uoj.ac/problem/228
sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧。

在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手。于是她的好朋友九条可怜酱给她出了一道题。

给出一个长度为 nn 的数列 AA,接下来有 mm 次操作,操作有三种:

对于所有的 i∈[l,r] 将 Ai 变成 Ai+x。
对于所有的 i∈[l,r] 将 Ai 变成 ⌊√Ai⌋
对于所有的 i∈[l,r] 询问 Ai 的和。

作为一个不怎么熟练的初学者,sylvia 想了好久都没做出来。而可怜酱又外出旅游去了,一时间联系不上。于是她决定向你寻求帮助:你能帮她解决这个问题吗。
输入格式

第一行两个数:n,mn,m。

接下来一行 nn 个数 AiAi。

接下来 mm 行中,第 ii 行第一个数 titi 表示操作类型:

若 ti=1ti=1,则接下来三个整数 li,ri,xili,ri,xi,表示操作一。

若 ti=2ti=2,则接下来三个整数 li,rili,ri,表示操作二。

若 ti=3ti=3,则接下来三个整数 li,rili,ri,表示操作三。
输出格式

对于每个询问操作,输出一行表示答案。
样例一
input

5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5

output

5
6

思路:这题暴力开根会TLE。考虑维护一个区间最大值MAX和区间最小值MIN,当 M A X = M I N MAX=MIN MAX=MIN或者 M A X − M A X = M I N − M I N MAX-\sqrt{MAX}=MIN-\sqrt{MIN} MAXMAX =MINMIN 时,区间开根操作就可以转换成区间加法操作,提高效率。

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+5;

struct node
{
    int l,r;
    ll sum,MAX,MIN,lazy;
};

node tree[maxn<<2];

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')
        ch=getchar();
    if(ch=='-')
        f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')
        x=x*10+ch-48,ch=getchar();
    return x*f;
}

inline void down(int i)
{
    int l=i<<1,r=i<<1|1;
    tree[l].lazy+=tree[i].lazy;
    tree[r].lazy+=tree[i].lazy;
    tree[l].sum+=(tree[l].r-tree[l].l+1)*tree[i].lazy;
    tree[r].sum+=(tree[r].r-tree[r].l+1)*tree[i].lazy;
    tree[l].MAX+=tree[i].lazy;
    tree[l].MIN+=tree[i].lazy;
    tree[r].MAX+=tree[i].lazy;
    tree[r].MIN+=tree[i].lazy;
    tree[i].lazy=0;
}

inline void up(int i)
{
    int l=i<<1,r=i<<1|1;
    tree[i].sum=tree[l].sum+tree[r].sum;
    tree[i].MAX=max(tree[l].MAX,tree[r].MAX);
    tree[i].MIN=min(tree[l].MIN,tree[r].MIN);
}

void build(int i,int l,int r)
{
    tree[i].l=l,tree[i].r=r;
    tree[i].lazy=0;
    if(l==r)
    {
        tree[i].MAX=tree[i].MIN=tree[i].sum=read();
        return ;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    up(i);
}

ll querysum(int i,int l,int r)
{
    if(tree[i].l==l&&tree[i].r==r)
        return tree[i].sum;
    if(tree[i].lazy)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(r<=mid)
        return querysum(i<<1,l,r);
    else if(l>mid)
        return querysum(i<<1|1,l,r);
    else
        return querysum(i<<1,l,mid)+querysum(i<<1|1,mid+1,r);
}

void update1(int i,int l,int r,ll v)
{
    if(tree[i].l==l&&tree[i].r==r)
    {
        tree[i].sum+=(r-l+1)*v;
        tree[i].MAX+=v;
        tree[i].MIN+=v;
        tree[i].lazy+=v;
        return ;
    }
    if(tree[i].lazy)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(r<=mid)
        update1(i<<1,l,r,v);
    else if(l>mid)
        update1(i<<1|1,l,r,v);
    else
        update1(i<<1,l,mid,v),
        update1(i<<1|1,mid+1,r,v);
    up(i);
}

void update2(int i,int l,int r)
{
    if(tree[i].l==l&&tree[i].r==r)
    {
        ll a=sqrt(tree[i].MAX);
        ll b=sqrt(tree[i].MIN);
        ll v=a-tree[i].MAX;
        int flag=0;
        if(tree[i].MAX==tree[i].MIN||tree[i].MAX-a==tree[i].MIN-b)
            flag=1;
        if(flag)
        {
            tree[i].sum+=(r-l+1)*v;
            tree[i].MAX+=v;
            tree[i].MIN+=v;
            tree[i].lazy+=v;
            return ;
        }
    }
    if(tree[i].lazy)
        down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(r<=mid)
        update2(i<<1,l,r);
    else if(l>mid)
        update2(i<<1|1,l,r);
    else
        update2(i<<1,l,mid),
        update2(i<<1|1,mid+1,r);
    up(i);
}

int main()
{
    int n,m,op,l,r;
    ll v;
    n=read(),m=read();
    build(1,1,n);
    for(int i=0;i<m;i++)
    {
        op=read(),l=read(),r=read();
        if(op==1)
        {
            v=read();
            update1(1,l,r,v);
        }
        else if(op==2)
            update2(1,l,r);
        else
            printf("%lld\n",querysum(1,l,r));
    }
    return 0;
}

你可能感兴趣的:(线段树)