HDU5828 Rikka with Sequence

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5828

Rikka with Sequence

Problem Description

As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

Yuta has an array A with n numbers. Then he makes m operations on it.

There are three type of operations:

1 l r x : For each i in [l,r], change A[i] to A[i]+x
2 l r : For each i in [l,r], change A[i] to ⌊A−−√[i]⌋
3 l r : Yuta wants Rikka to sum up A[i] for all i in [l,r]

It is too difficult for Rikka. Can you help her?

Input

The first line contains a number t(1<=t<=100), the number of the testcases. And there are no more than 5 testcases with n>1000.

For each testcase, the first line contains two numbers n,m(1<=n,m<=100000). The second line contains n numbers A[1]~A[n]. Then m lines follow, each line describe an operation.

It is guaranteed that 1<=A[i],x<=100000.

Output

For each operation of type 3, print a lines contains one number – the answer of the query.

Sample Input

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

Sample Output

5
6

题解

根据上帝造题的7分钟的思路,开根操作会让区间内的数变得越来越一样,所以不妨记录一下区间最大值和最小值的大小以及个数,这样在 maxmin1 m a x − m i n ≤ 1 的时候我们就可以直接计算出开根操作对区间的影响,当开根后 max=min m a x = m i n 时还需要一个区间赋值操作。

代码
#include<bits/stdc++.h>
#define ll long long
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=4e5+5;
ll que[M],mx[M],mn[M],cmax[M],cmin[M],sum[M],tag[M],same[M];
int n,m;
void in(){scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%lld",&que[i]);}
void up(int v)
{
    sum[v]=sum[ls]+sum[rs],cmax[v]=cmin[v]=0;
    mx[v]=max(mx[ls],mx[rs]),mn[v]=min(mn[ls],mn[rs]);
    if(mx[ls]>=mx[rs])cmax[v]+=cmax[ls];if(mx[ls]<=mx[rs])cmax[v]+=cmax[rs];
    if(mn[ls]<=mn[rs])cmin[v]+=cmin[ls];if(mn[ls]>=mn[rs])cmin[v]+=cmin[rs];
}
void push(int v,int le,int ri)
{
    int mid=le+ri>>1;
    if(~same[v])
    {
        same[ls]=same[rs]=same[v],mx[ls]=mx[rs]=mn[ls]=mn[rs]=same[v],tag[ls]=tag[rs]=0,same[v]=-1;
        sum[ls]=same[ls]*(cmax[ls]=cmin[ls]=mid-le+1),sum[rs]=same[rs]*(cmax[rs]=cmin[rs]=ri-mid);
    }
    if(tag[v])
    {
        tag[ls]+=tag[v],mx[ls]+=tag[v],mn[ls]+=tag[v],tag[rs]+=tag[v],mx[rs]+=tag[v],mn[rs]+=tag[v];
        sum[ls]+=(mid-le+1)*tag[v],sum[rs]+=(ri-mid)*tag[v];
        tag[v]=0;
    }
}
void build(int v,int le,int ri)
{
    same[v]=-1;tag[v]=0;
    if(le==ri){mx[v]=mn[v]=sum[v]=que[le],cmax[v]=cmin[v]=1;return;}
    int mid=le+ri>>1;
    build(ls,le,mid);build(rs,mid+1,ri);
    up(v);
}
void add(int v,int le,int ri,int lb,int rb,int d)
{
    if(lb<=le&&ri<=rb){tag[v]+=d,mx[v]+=d,mn[v]+=d,sum[v]+=1ll*(ri-le+1)*d;return;}
    int mid=le+ri>>1;push(v,le,ri);
    if(lb<=mid)add(ls,le,mid,lb,rb,d);if(mid<rb)add(rs,mid+1,ri,lb,rb,d);
    up(v);
}
void root(int v,int le,int ri,int lb,int rb)
{
    if(le==ri){sum[v]=mx[v]=mn[v]=(ll)sqrt(mx[v]);return;}
    if(lb<=le&&ri<=rb&&mx[v]-mn[v]<=1)
    {
        if(mx[v]==mn[v])same[v]=mx[v]=mn[v]=(ll)sqrt(mn[v]),cmax[v]=cmin[v]=ri-le+1,sum[v]=mx[v]*cmax[v],tag[v]=0;
        else
        {
            ll pre=mx[v];mx[v]=(ll)sqrt(mx[v]),mn[v]=(ll)sqrt(mn[v]);
            if(mx[v]==mn[v])same[v]=mx[v],cmax[v]=cmin[v]=ri-le+1,sum[v]=mx[v]*cmax[v],tag[v]=0;
            else tag[v]+=mx[v]-pre,sum[v]=mx[v]*cmax[v]+mn[v]*cmin[v];
        }
        return;
    }
    int mid=le+ri>>1;push(v,le,ri);
    if(lb<=mid)root(ls,le,mid,lb,rb);if(mid<rb)root(rs,mid+1,ri,lb,rb);
    up(v);
}
ll ask(int v,int le,int ri,int lb,int rb)
{
    if(lb<=le&&ri<=rb){return sum[v];}
    int mid=le+ri>>1;ll ans=0;push(v,le,ri);
    if(lb<=mid)ans=ask(ls,le,mid,lb,rb);if(mid<rb)ans+=ask(rs,mid+1,ri,lb,rb);
    up(v);return ans;
}
void ac()
{
    build(1,1,n);
    int op,l,r,x;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&op,&l,&r);
        if(op==1){scanf("%d",&x);add(1,1,n,l,r,x);}
        else if(op==2)root(1,1,n,l,r);
        else printf("%lld\n",ask(1,1,n,l,r));
    }
}
int main(){int T;scanf("%d",&T);while(T--)in(),ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值