HDU5828(线段树好题,区间加,区间求根号,吉老师在51nod直播讲过这道题,相应的还有区间取模)

题解:区间加,区间和不用说。重点在区间取模,首先,就算一个很大的n,几次根号之后就会很小。

我们可以思考一下,如果一个区间内的极差>1的时候,不断地进行整体加某个值然后开方,是没办法保持住这样的序列的(相邻两个极差都>1)。只有整个区间内的极差<=1的时候,才能起到这种效果。所以我们就在线段树上再增加一些信息。就是最大值和最小值,最大值的个数,最小值的个数。这样的话,如果区间内的极差==1的时候,我们也能直接对整段进行操作。就能处理前面的这种样例了。


区间的极差==1的时候。那么这种情况开方以后有两种情况。1:整个区间相等了。2:整个区间的极差还是1。
对于第一种情况。我们只要加一个cover标记,加一个区间覆盖的标记就可以解决了。

对于第二种情况,相当于,区间减去了一个相等的值,修改一下区间增加的标记就可以了。然后在pushdown的时候增加一下cover的情况。

证明:

假设某一段区间的最大值x,和最小值y.
如果极差>1,那么这个极差经过很少的次数开根号, 其中的极差也会变得越来越小,
用公式来表达 假设当前这段区间要+d,然后再开根号.
必然有这样的式子

x+dy+d<=xy

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 100010
#define Mm 2000005
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
ll a[Mn];
ll sum[Mn*4];
ll maxx[Mn*4],minn[Mn*4];
ll maxNum[Mn*4],minNum[Mn*4];
ll all[Mn*4];
ll lazy[Mn*4];
void pushUp(int u) {
    sum[u]=sum[ul]+sum[ur];
    maxx[u]=max(maxx[ul],maxx[ur]);
    minn[u]=min(minn[ul],minn[ur]);
    maxNum[u]=minNum[u]=0;
    if(maxx[u]==maxx[ul]) maxNum[u]+=maxNum[ul];
    if(maxx[u]==maxx[ur]) maxNum[u]+=maxNum[ur];
    if(minn[u]==minn[ul]) minNum[u]+=minNum[ul];
    if(minn[u]==minn[ur]) minNum[u]+=minNum[ur];
}
void pushDown(int u,int l,int r) {
    if(all[u]) {
        int mid=(l+r)>>1;
        all[ul]=all[ur]=all[u];
        sum[ul]=all[u]*(mid-l+1);
        sum[ur]=all[u]*(r-mid);
        maxx[ul]=maxx[ur]=minn[ul]=minn[ur]=all[u];
        maxNum[ul]=minNum[ul]=mid-l+1;
        maxNum[ur]=minNum[ur]=r-mid;
        lazy[ul]=lazy[ur]=0;
        all[u]=0;
    }
    if(lazy[u]) {
        int mid=(l+r)>>1;
        lazy[ul]+=lazy[u];lazy[ur]+=lazy[u];
        sum[ul]+=lazy[u]*(mid-l+1);
        sum[ur]+=lazy[u]*(r-mid);
        maxx[ul]+=lazy[u];minn[ul]+=lazy[u];
        maxx[ur]+=lazy[u];minn[ur]+=lazy[u];
        lazy[u]=0;
    }
}
void build(int l,int r,int u) {
    all[u]=lazy[u]=maxx[u]=minn[u]=sum[u]=0;
    if(l==r) {
        maxx[u]=minn[u]=sum[u]=a[l];
        maxNum[u]=minNum[u]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ul);
    build(mid+1,r,ur);
    pushUp(u);
}
int s,t;
void add(int l,int r,int u,ll x) {
    if(s<=l&&t>=r) {
        sum[u]+=x*(r-l+1);
        minn[u]+=x;
        maxx[u]+=x;
        lazy[u]+=x;
        return ;
    }
    pushDown(u,l,r);
    int mid=(l+r)>>1;
    if(t<=mid) add(l,mid,ul,x);
    else if(s>mid) add(mid+1,r,ur,x);
    else{add(l,mid,ul,x);add(mid+1,r,ur,x);}
    pushUp(u);
}
void Sqrt(int l,int r,int u) {
    if(s<=l&&t>=r&&(maxx[u]-minn[u]<=1)) {
        if(maxx[u]==1) return ;
        ll x=maxx[u];
        if(maxx[u]==minn[u]) {
            minn[u]=maxx[u]=floor(sqrt(x));
            lazy[u]+=maxx[u]-x;
            sum[u]=maxx[u]*(r-l+1);
            return ;
        } else if(maxx[u]-minn[u]==1){
            maxx[u]=floor(sqrt(maxx[u]));
            minn[u]=floor(sqrt(minn[u]));
            if(maxx[u]==minn[u]) {
                maxNum[u]=minNum[u]=r-l+1;
                lazy[u]=0;
                all[u]=maxx[u];
                sum[u]=maxx[u]*maxNum[u];
            } else if(maxx[u]-minn[u]==1) {
                lazy[u]+=maxx[u]-x;
                sum[u]=maxx[u]*maxNum[u]+minn[u]*minNum[u];
            }
            return ;
        }
        return ;
    }
    pushDown(u,l,r);
    int mid=(l+r)>>1;
    if(t<=mid) Sqrt(l,mid,ul);
    else if(s>mid)  Sqrt(mid+1,r,ur);
    else{Sqrt(l,mid,ul);Sqrt(mid+1,r,ur);}
    pushUp(u);
}
ll query(int l,int r,int u) {
    if(s<=l&&t>=r)
        return sum[u];
    pushDown(u,l,r);
    int mid=(l+r)>>1;
    if(t<=mid) return query(l,mid,ul);
    else if(s>mid) return query(mid+1,r,ur);
    else return query(l,mid,ul)+query(mid+1,r,ur);;
}
int main() {
    int T,k;
    scanf("%d",&T);
    while(T--) {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build(1,n,1);
        while(m--) {
            scanf("%d%d%d",&k,&s,&t);
            if(k==1) {
                ll x;scanf("%lld",&x);
                add(1,n,1,x);
            } else if(k==2){
                Sqrt(1,n,1);
            } else {
                printf("%lld\n",query(1,n,1));
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值