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

题目大意

维护一个序列,支持三种操作
1、区间加法
2、区间开方下取整
3、查询区间和

线段树

有一道bzoj上的名为上帝造题的七分钟的题目没有加法操作。
有了我们怎么做呢?
假设一个区间所有数都相同,显然对它开方就是一个赋值标记嘛。
存在不同呢?
对于两个数a和b设a大于b。
原本它们的差是a-b
开方后差变成了 ab=aba+b
我们又知道 ab<a+b
相当于每次把差变成比原来的开方还小了。
那就和bzoj那道差不多了。
对一个数不停开方一直开方到开方不会再产生变化只需要很少的步数。
因此对于一个区间最大值不等于最小值的情况,可以考虑暴力递归左右。
不过可以注意到两个数的差开方可能会开方到1然后也不再变化了。
因此区间的最大值和最小值只相差1时也需要讨论开方是否会使得它们的差产生变化,不产生变化的话可以直接打加法标记。
这样就可以做了。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
ll mx[maxn*4],mi[maxn*4],sum[maxn*4],ad[maxn*4],st[maxn*4];
int a[maxn*4];
bool bz[maxn*4];
int i,j,k,l,r,x,t,n,m;
ll ans;
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;
}
void build(int p,int l,int r){
    if (l==r){
        sum[p]=mx[p]=mi[p]=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);build(p*2+1,mid+1,r);
    sum[p]=sum[p*2]+sum[p*2+1];
    mx[p]=max(mx[p*2],mx[p*2+1]);
    mi[p]=min(mi[p*2],mi[p*2+1]);
}
void markst(int p,int l,int r,ll v){
    ad[p]=0;
    mx[p]=mi[p]=v;
    sum[p]=(ll)(r-l+1)*v;
    bz[p]=1;
    st[p]=v;
}
void markad(int p,int l,int r,ll v){
    ad[p]+=v;
    mx[p]+=v;
    mi[p]+=v;
    sum[p]+=(ll)(r-l+1)*v;
}
void down(int p,int l,int r){
    int mid=(l+r)/2;
    if (bz[p]){
        markst(p*2,l,mid,st[p]);
        markst(p*2+1,mid+1,r,st[p]);
        bz[p]=0;
    }
    if (ad[p]){
        markad(p*2,l,mid,ad[p]);
        markad(p*2+1,mid+1,r,ad[p]);
        ad[p]=0;
    }
}
void change(int p,int l,int r,int a,int b,int v){
    if (l==a&&r==b){
        markad(p,l,r,v);
        return;
    }
    int mid=(l+r)/2;
    down(p,l,r);
    if (b<=mid) change(p*2,l,mid,a,b,v);
    else if (a>mid) change(p*2+1,mid+1,r,a,b,v);
    else{
        change(p*2,l,mid,a,mid,v);
        change(p*2+1,mid+1,r,mid+1,b,v);
    }
    sum[p]=sum[p*2]+sum[p*2+1];
    mx[p]=max(mx[p*2],mx[p*2+1]);
    mi[p]=min(mi[p*2],mi[p*2+1]);
}
void kf(int p,int l,int r,int a,int b){
    if (l==a&&r==b){
        if (mx[p]==mi[p]){
            markst(p,l,r,int(sqrt(mx[p])));
            return;
        }
        else if (mx[p]-mi[p]==1){
            if (int(sqrt(mi[p]))==int(sqrt(mx[p]))) markst(p,l,r,int(sqrt(mx[p])));
            else markad(p,l,r,int(sqrt(mi[p]))-mi[p]);
            return;
        }
        down(p,l,r);
        int mid=(l+r)/2;
        kf(p*2,l,mid,a,mid);kf(p*2+1,mid+1,r,mid+1,b);
        sum[p]=sum[p*2]+sum[p*2+1];
        mx[p]=max(mx[p*2],mx[p*2+1]);
        mi[p]=min(mi[p*2],mi[p*2+1]);
        return;
    }
    down(p,l,r);
    int mid=(l+r)/2;
    if (b<=mid) kf(p*2,l,mid,a,b);
    else if (a>mid) kf(p*2+1,mid+1,r,a,b);
    else{
        kf(p*2,l,mid,a,mid);
        kf(p*2+1,mid+1,r,mid+1,b);
    }
    sum[p]=sum[p*2]+sum[p*2+1];
    mx[p]=max(mx[p*2],mx[p*2+1]);
    mi[p]=min(mi[p*2],mi[p*2+1]);
}
ll query(int p,int l,int r,int a,int b){
    if (l==a&&r==b) return sum[p];
    down(p,l,r);
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b);
    else return query(p*2,l,mid,a,mid)+query(p*2+1,mid+1,r,mid+1,b);
}
int main(){
    n=read();m=read();
    fo(i,1,n) a[i]=read();
    build(1,1,n);
    while (m--){
        t=read();
        if (t==1){
            l=read();r=read();x=read();
            change(1,1,n,l,r,x);
        }
        else if (t==2){
            l=read();r=read();
            kf(1,1,n,l,r);
        }
        else{
            l=read();r=read();
            ans=query(1,1,n,l,r);
            printf("%lld\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值