【HDU 5828】Rikka with Sequence(线段树维护区间开方,修改,求和)

首先我们知道一个数开7 8次方过后就歇逼了

那我们是否可以维护一个区间的flag标记 表示是否一个区间里的数都是一样的 那开根就到这儿就行了(似乎在口胡?)

然后我们看一组数据

2 3 2 3 2 3

然后整体加6然后开根号,又会变回2 3 2 3 2 3

相当于不会出现区间数字一样的情况了,这样就相当于没剪枝,就T了

然后发现只有极差为1的时候才会出现这种情况,极差>1的话是不可能维持原来的序列的,那么就可以维护一个最大值和最小值,如果极差为1的话那么区间开根号其实相当于区间减

去了x-sqrt(x),再加上第一个版本的剪枝就可以2000MS左右过去了

如果极差为0 那就是区间覆盖了

#include<bits/stdc++.h>
#define N 100005
#define int long long
using namespace std;
int n,Q,w[N];
struct Node
{
    int l,r,minv,maxv,val,lazy;
}tree[4*N];
inline void pushup(int now)
{
    tree[now].maxv=max(tree[2*now].maxv,tree[2*now+1].maxv);
    tree[now].minv=min(tree[2*now].minv,tree[2*now+1].minv);
    tree[now].val=tree[2*now].val+tree[2*now+1].val;
}
inline void pushdown(int now)
{
    if(tree[now].lazy)
    {
        tree[2*now].lazy+=tree[now].lazy;    tree[2*now+1].lazy+=tree[now].lazy;
        tree[2*now].maxv+=tree[now].lazy;    tree[2*now+1].maxv+=tree[now].lazy;
        tree[2*now].minv+=tree[now].lazy;    tree[2*now+1].minv+=tree[now].lazy;
        tree[2*now].val+=tree[now].lazy*(tree[2*now].r-tree[2*now].l+1);
        tree[2*now+1].val+=tree[now].lazy*(tree[2*now+1].r-tree[2*now+1].l+1);
        tree[now].lazy=0;
    }
    if(tree[now].minv==tree[now].maxv)    //特判区间覆盖 
    {
        tree[2*now].minv=tree[2*now].maxv=tree[2*now+1].minv=tree[2*now+1].maxv=tree[now].minv;
        tree[2*now].val=tree[now].minv*(tree[2*now].r-tree[2*now].l+1);
        tree[2*now+1].val=tree[now].minv*(tree[2*now+1].r-tree[2*now+1].l+1);
    }
}
void build(int now,int l,int r)
{
    tree[now].l=l,tree[now].r=r;
    if(l==r)
    {
        tree[now].minv=w[l];
        tree[now].maxv=w[l];
        tree[now].val=w[l];
        return;
    }
    int m=(l+r)>>1;
    build(2*now,l,m);
    build(2*now+1,m+1,r);
    pushup(now);
}
void add(int now,int l,int r,int del)
{
//    cout<<now<<" "<<tree[now].l<<" "<<tree[now].r<<endl;
    if(l<=tree[now].l&&tree[now].r<=r)
    {
        tree[now].minv+=del;
        tree[now].maxv+=del;
        tree[now].val+=(tree[now].r-tree[now].l+1)*del;
        tree[now].lazy+=del; 
        return;
    }
    pushdown(now);
    int m=(tree[now].l+tree[now].r)>>1;
    if(l<=m)    add(2*now,l,r,del);
    if(r>m)    add(2*now+1,l,r,del);
    pushup(now);
}
void Sqrt(int now,int l,int r)
{
//    cout<<now<<" "<<tree[now].l<<" "<<tree[now].r<<endl;
    if(l<=tree[now].l&&tree[now].r<=r)
    {
        if((int)sqrt(tree[now].minv)==(int)sqrt(tree[now].maxv))    //覆盖
        {
            tree[now].minv=tree[now].maxv=(int)sqrt(tree[now].minv);
            tree[now].val=tree[now].minv*(tree[now].r-tree[now].l+1);
        } 
        else if(tree[now].minv+1==tree[now].maxv)
        {
            int k=tree[now].maxv-(int)sqrt(tree[now].maxv);
            add(now,l,r,-k);
        }
        else    //暴力改 
        {
            pushdown(now);
            int m=(tree[now].l+tree[now].r)>>1;
            if(l<=m) Sqrt(2*now,l,r);
            if(r>m)    Sqrt(2*now+1,l,r);
            pushup(now);
        }
        return;
    }
    pushdown(now);
    int m=(tree[now].l+tree[now].r)>>1;
    if(l<=m) Sqrt(2*now,l,r);
    if(r>m)    Sqrt(2*now+1,l,r);
    pushup(now);
}
inline int query(int now,int l,int r)
{
    if(l<=tree[now].l&&tree[now].r<=r)
    {
        return tree[now].val;
    }
    pushdown(now);
    int m=(tree[now].l+tree[now].r)>>1;
    int ans=0;
    if(l<=m) ans+=query(2*now,l,r);
    if(r>m)    ans+=query(2*now+1,l,r);
    return ans;
}
main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);cout.tie(NULL);
    int T;
    cin>>T;
    while(T--)
    {
        memset(w,0,sizeof(w));
        memset(tree,0,sizeof(tree));
        cin>>n>>Q;
        for(int i=1;i<=n;i++)    cin>>w[i];
        build(1,1,n);
        for(int i=1;i<=Q;i++)
        {
            int opt,x,y,del;
            cin>>opt;
            if(opt==1)    //区间修改 
            {
                cin>>x>>y>>del;
                add(1,x,y,del);
            }
            if(opt==2)
            {
                cin>>x>>y;
                Sqrt(1,x,y);
            } 
            if(opt==3)
            {
                cin>>x>>y;
                cout<<query(1,x,y)<<'\n';
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值