HDU5828 区间开根加法求和 线段树

先需要想到一个结论:

对于线段树上的一块,进行整体开根和加法(加法不影响结果)的次数越多,区间的最大值与最小值的差越小。

利用这个性质,我们维护一棵线段树,线段树上维护区间最大值、最小值、和、元素个数。

对于建树、区间加法(修改)、区间查询,写法就是朴素的线段树

对于区间开根:

1、若区间最大值和最小值的差为0,即整个区间的所有元素相等。此时开根相当于区间覆盖同一个值。

2、若区间最大值和最小值的差为1,那么分为两种情况(可以简单地证明不会存在别的情况)

(1) sqrt( 最大值 ) - sqrt( 最小值 ) = 1 , 此时开根相当于区间减法。

(2) sqrt( 最大值 ) - sqrt ( 最小值) = 0 , 此时开根相当于区间覆盖。

3、若区间最大值和最小值的差大于等于2,那么继续向下递归暴力修改合并。


Tips:

1、3秒时间要跑100组数据,注意线段树的常数。可以考虑读入、输出优化。

2、long long和开根的精度

3、若一个线段树结点的左儿子和右儿子被相同的树区间覆盖,此时需要将标记上传,否则严重影响效率。

#include <cassert>
#include <iostream>
#include <cstdio>
#include <cmath>

//#define mid (l + r) >> 1 
#define ls (t << 1)
#define rs ((t<<1) | 1)

#define N 100050

using namespace std;

typedef long long LL;

struct Tree{ LL max,min,sum,siz; }tr[4*N];
LL A[N],tag[4*N],tag2[4*N];
LL n,m;
LL ans = 0LL;

inline void read(LL &x) {
    static char c;
    while(!isdigit(c = getchar()));
    x = c - '0';
    while( isdigit(c = getchar()))
        x = x * 10 + (c - '0');
}

void out(LL a) {
    if(a > 9) out(a / 10);
    putchar(a % 10 + '0');
}

inline Tree merge(Tree p1,Tree p2,int t) {
    Tree tmp;
    if (tag2[ls] == tag2[rs] && tag2[ls] != -1) {
    	tag2[t] = tag2[ls];
    	tag[t] = 0;
    } else tag2[t] = -1;
    tmp.max = max( p1.max , p2.max );
    tmp.min = min( p1.min , p2.min );
    tmp.sum = p1.sum + p2.sum;
    tmp.siz = p1.siz + p2.siz;
    return tmp;
}

void build(int l,int r,int t) {
    tag[t] = 0;
    if (l == r) {
        tr[t].max = tr[t].min = A[l];
        tr[t].sum = 1LL * A[l];
        tr[t].siz = 1;
        tag2[t] = A[l];
        return ;
    }
    tag2[t] = -1;
    int mid = (l + r) >> 1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    tr[t] = merge(tr[ls],tr[rs],t);
}

inline void color(int t,LL v) {
    tr[t].max += v;
    tr[t].min += v;
    tr[t].sum += 1LL * tr[t].siz * v;
    return ;
} 

inline void color2(int t,LL v) {
    tr[t].max = v;
    tr[t].min = v;
    tr[t].sum = 1LL * tr[t].siz * v;
    return ;
}

inline void push_down(int t) {
    if (tag[t] == 0  && tag2[t] == -1) return ;
    assert(!(tag[t] != 0 && tag2[t] != -1));
    if (tag2[t] != -1) {
        tag[ls] = tag[rs] = 0LL;
        tag2[ls] = tag2[t];
            color2(ls,tag2[t]);
        
        tag2[rs] = tag2[t];
            color2(rs,tag2[t]);
    } else {
        if (tag2[ls] != -1) 
            tag2[ls] += tag[t]; 
        else 
            tag[ls] += tag[t];
        color(ls,tag[t]);

        if (tag2[rs] != -1) 
            tag2[rs] += tag[t];
        else
            tag[rs] += tag[t];
        color(rs,tag[t]);
    }
    tag2[t] = -1; tag[t] = 0;
    return ;
}

LL ll,rr; LL v;
void update_sqrt(int l,int r,int t) {
    //assert(l <= rr && r >= ll);
    //if (l > rr || r < ll) return ;
    if (l >= ll && r <= rr) {
        if (tag2[t] != -1) {
            tag2[t] = 1LL * sqrt( tag2[t] );
            tr[t].max = tr[t].min = tag2[t];
            tr[t].sum = tr[t].siz * tag2[t];
            return ;
        }
        LL p1 = sqrt( tr[t].max );
        LL p2 = sqrt( tr[t].min );
        if (tr[t].max - tr[t].min == 1) {
            if (p1 - p2 == 1) {
                LL v = p1 - tr[t].max;
                tag[t] += v;
                color(t,v);
            } else {
                LL v = p1;
                tag[t] = 0;
                tag2[t] = v;
                color2(t,v);
            }
            return ;
        }
    }
    push_down(t);
    int mid = (l + r) >> 1;
    if (ll <= mid) update_sqrt(l,mid,ls);
    if (rr > mid) update_sqrt(mid+1,r,rs);
    tr[t] = merge(tr[ls],tr[rs],t);
}

void update_add(int l,int r,int t) {
    //assert(l <= rr && r >= ll);
    if (l >= ll && r <= rr) {
        if (tag2[t] != -1) 
            tag2[t] += v; 
        else 
            tag[t] += v;
        color(t,v);
        return ;
    }
    push_down(t);
    int mid = (l + r) >> 1;
    if (ll <= mid)
        update_add(l,mid,ls);
    if (rr > mid)
        update_add(mid+1,r,rs);
    tr[t] = merge(tr[ls],tr[rs],t); 
}

void query(int l,int r,int t) {
    //assert(l <= rr && r >= ll);
    if (l >= ll && r <= rr) { ans += 1LL * tr[t].sum; return; }
    push_down(t);
    int mid = (l + r) >> 1;
    if (ll <= mid) query(l,mid,ls);
    if (rr > mid) query(mid+1,r,rs);
    tr[t] = merge(tr[ls],tr[rs],t);
}

int main()
{
    LL T = 0; read(T);
    while (T--) {
        //memset(tr,0,sizeof(tr));
        //memset(tag,0,sizeof(tag));
        //memset(tag2,255,sizeof(tag2));
        read(n); read(m);
        for (int i=1;i<=n;i++) read(A[i]);
        build(1,n,1);
        while (m--) {
            LL cmd = 0; read(cmd); read(ll); read(rr);
            switch(cmd) {
                case 1: { read(v); update_add(1,n,1); break;    }
                case 2: { update_sqrt(1,n,1); break; }
                case 3: { ans = 0LL; query(1,n,1); out(ans); printf("\n"); break; }
                assert(0);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值