Hdu5828-Rikka with Sequence(线段树区间开方)

写在前面

ldx神犇今讲线段树,然后就涉及到了本题目的一个骚操作。

传送门

Solution

注意到本题实际上要求的是区间加,区间求和,区间开根号。前两个是常规操作,学了都会,然而怎样开根号?显然我们应该暴力修改对开方这个操作本身进行考虑。那么注意到,开一次方,一个序列中的每个数逐步接近其他的数,所以说我们可以考虑这么干:对序列维护一个最大值与最小值,如果说他们开方以后最大值减去最小值的差大于1,那么我们就认为这些数还不够接近,所以说直接递归修改这些区间,如果说这个差小于等于1,那么我们就继续暴力讨论一下。如果说这个差等于0,那么区间开方操作就相当于一次区间覆盖操作,这是常规操作,如果大于0小于等于1,那么可以看作一次区间减操作,这也很常规,那么这样我们就把题目分析完了。

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
typedef long long ll;
ll read(){
    ll sum=0,neg=1;
    char c=getchar();
    while(c>'9'||c<'0'&&c!='-') c=getchar();
    if(c=='-') neg=-1,c=getchar();
    while(c>='0'&&c<='9') sum=(sum<<1)+(sum<<3)+c-'0',c=getchar();
    return sum*neg;
}
ll n,m,T,a[N];
struct Node{
    int l,r;
    ll sum,minn,maxn,lazy,tag;
};
struct SegmentTree{
    Node T[N*4];
    void pushup(int p){
        T[p].maxn=max(T[lc].maxn,T[rc].maxn);
        T[p].minn=min(T[lc].minn,T[rc].minn);
        T[p].sum=T[lc].sum+T[rc].sum;
    }
    void pushnow(int p,ll v){
        T[p].sum+=(T[p].r-T[p].l+1)*v;
        T[p].lazy+=v;
        T[p].maxn+=v;
        T[p].minn+=v;
    }
    void pushcover(int p,ll v){
        T[p].lazy=0;
        T[p].sum=(T[p].r-T[p].l+1)*v;
        T[p].tag=T[p].maxn=T[p].minn=v;
    }
    void pushdown(int p){
        if(T[p].tag!=-1){
            pushcover(lc,T[p].tag);
            pushcover(rc,T[p].tag);
            T[p].tag=-1;
        }
        if(T[p].lazy){
            pushnow(lc,T[p].lazy);
            pushnow(rc,T[p].lazy);
            T[p].lazy=0;
        }
    }
    void build(int p,int l,int r){
        T[p].l=l; T[p].r=r; T[p].lazy=0; T[p].tag=-1;
        if(l==r){
            T[p].maxn=T[p].minn=T[p].sum=a[l];
            return;
        }
        build(lc,l,mid);
        build(rc,mid+1,r);
        pushup(p);
    }
    void update(int p,int ql,int qr,ll v){
        if(ql>T[p].r||qr<T[p].l) return;
        if(ql<=T[p].l&&T[p].r<=qr){
            pushnow(p,v);
            return;
        }
        pushdown(p);
        if(qr<=mid) update(lc,ql,qr,v);
        else if(ql>mid) update(rc,ql,qr,v);
        else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
        pushup(p);
    }
    void modify(int p,int ql,int qr){
        if(ql>T[p].r||qr<T[p].l) return;
        if(ql<=T[p].l&&T[p].r<=qr&&T[p].maxn-T[p].minn<=1){
            ll fx=sqrt(T[p].maxn),fy=sqrt(T[p].minn);
            if(fx==fy) pushcover(p,fx);
            else pushnow(p,fx-T[p].maxn);
            return;
        }
        pushdown(p);
        if(qr<=mid)modify(lc,ql,qr);
        else if(ql>mid)modify(rc,ql,qr);
        else modify(lc,ql,mid),modify(rc,mid+1,qr);
        pushup(p);
    }
    ll query(int p,int ql,int qr){
        if(ql>T[p].r||qr<T[p].l) return 0;
        if(ql<=T[p].l&&T[p].r<=qr) return T[p].sum;
        pushdown(p);
        if(qr<=mid) return query(lc,ql,qr);
        if(ql>mid) return query(rc,ql,qr);
        return query(lc,ql,mid)+query(rc,mid+1,qr);
    }
}Tree;
int main(){
    T=read();
    while(T--){
        n=read(); m=read();
        for(int i=1;i<=n;i++) a[i]=read();
        Tree.build(1,1,n);
        while(m--){
            int opt,l,r;
            opt=read(); l=read(); r=read();
            switch(opt){
                case 1:{
                    ll v=read();
                    Tree.update(1,l,r,v);
                    break;
                }
                case 2:{
                    Tree.modify(1,l,r);
                    break;
                }
                default:{
                    printf("%lld\n",Tree.query(1,l,r));
                    break;
                }
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值