hdu5828 Rikka with Sequence

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5828

题意就是维护一个序列,总共有三种操作:1,在l到r这个区间上加一个数字、2,把l到r这个区间的每个数字都开根号(向下取整)、3,查询l到r这个区间的和。

由于有开根号这个操作,很难对一段连续的数字进行操作。由于开根号开了几次之后很容易把整段数字变得很小,之后加上数字 后或者再继续开根号时,很有可能一整段都是相等的。那么可以想到一个剪枝就是,维护这个区间的最大值最小值,假如最大值和最小值开根号得到的数字相同,那么可以整段进行操作。但是还有一种情况,就是3,4,3,4,3……的时候,如果对这个区间不停地加2,开根号,加2,开根号,那么最后还是会出现o(n²) 的情况。然后我们再想到一个剪枝,就是当最小值最大值相差1的时候,如果开根号的结果也相差1,那么我们可以视为给这段区间同时减去一个数字。这个线段树我们维护一下,区间最值,区间和,以及lazy,cover。

这也算是我写的第一道比较复杂的线段树吧。

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG  MOD=1e9+61;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, n , 1
const int MAXN = 1e5 + 10 ;
struct Tree{
    LONG Sum , Max , Min;
}tree[MAXN<<2];

LONG lazy[MAXN<<2] ;
LONG  cov[MAXN<<2] ;
void Push_up(int rt)
{
    tree[rt].Sum = tree[rt<<1].Sum + tree[rt<<1|1].Sum;
    tree[rt].Max = max(tree[rt<<1].Max , tree[rt<<1|1].Max) ;
    tree[rt].Min = min(tree[rt<<1].Min , tree[rt<<1|1].Min ) ;
}
void build_tree( int l ,int r , int rt)
{
    lazy[rt] = 0 ;
    cov[rt] = 0 ;
    if(l == r)
    {
        scanf("%lld", &tree[rt].Sum) ;
        tree[rt].Max = tree[rt].Sum ;
        tree[rt].Min = tree[rt].Sum ;
        lazy[rt] = tree[rt].Sum ;
        cov[rt] = 0;
        return  ;
    }
    int mid = (l +r) /2 ;
    build_tree(lson) ;
    build_tree(rson) ;
    Push_up(rt) ;
}
void Push_down(int l , int r , int rt)
{
    if(cov[rt] > 0 )
    {
        int mid = ( l +r )/2 ;
        tree[rt<<1].Sum = (LONG )(mid - l + 1)* cov[rt] ;
        tree[rt<<1|1].Sum = (LONG )( r - mid) * cov[rt]  ;
        tree[rt<<1|1].Max = tree[rt<<1].Max = tree[rt<<1|1].Min = tree[rt<<1].Min = cov[rt<<1] = cov[ rt<<1|1] = cov[rt] ;
        cov[rt] =  lazy[rt<<1|1] = lazy[rt<<1] = 0 ;
        //这里不能有return
    }
    if(lazy[rt] != 0)  // rt的lazy本质上是给lazy的儿子用的
    {
        int mid = (l + r) /2 ;
        lazy[rt<<1] += lazy[rt] ;
        lazy[rt<<1|1] += lazy[rt] ;
        tree[rt<<1].Sum += (LONG )(mid - l + 1) * lazy[rt] ;
        tree[rt<<1|1].Sum += (LONG )(r - mid) * lazy[rt] ;
        tree[rt<<1].Max += lazy[rt] ;
        tree[rt<<1|1].Max += lazy[rt] ;
        tree[rt<<1].Min += lazy[rt] ;
        tree[rt<<1 | 1].Min += lazy[rt] ;
        lazy[rt] = 0 ;
    }
}
void Add(int L ,int R , int l , int r , int rt , LONG val )
{
    if(L <= l && r <= R)
    {
        tree[rt].Sum += (LONG ) (r - l + 1)* val ;
        tree[rt].Max += val ;
        tree[rt].Min += val ;
        lazy [rt] += val ;
        return ;
    }
    int mid = (l + r) / 2;
    Push_down(l , r , rt) ;
    if(L <= mid)
    Add( L , R , lson ,val) ;
    if(R > mid )
    Add(L , R , rson , val ) ;
    Push_up( rt) ;
}
void Update( int L , int R , int l , int  r , int rt )
{
    if(L <= l&& r <= R)
    {
    if(tree[rt].Max == 1 ) return ;
    if( r == l)
    {
        LONG tmp = (LONG )floor((sqrt( tree[rt].Sum )))  ;
        tree[rt].Sum =
        tree[rt].Max =
        tree[rt].Min = tmp ;
        return ;
    }
    if( tree[rt].Max == tree[rt].Min)
    {
        LONG temp = tree[rt].Min  ;
        tree[rt].Max = tree[rt].Min = (LONG) floor(sqrt(temp)) ;
        tree[rt].Sum = (LONG ) tree[rt].Max * (r- l + 1) ;
        lazy [rt] +=  tree[rt].Min -temp ;
        return ;
    }
    if(tree[rt].Max - tree[rt].Min == 1)
    {
        LONG tmp1 =(LONG )floor( sqrt(tree[rt].Min )  ) ;
        LONG tmp2 = (LONG) floor(sqrt(tree[rt].Max) );
        if(tmp2 - tmp1 ==  1)
        {
            lazy[rt] +=  tmp1 - tree[rt].Min   ;
            tree[rt].Sum += (LONG) (r - l + 1)*(tmp1 - tree[rt].Min ) ;
            tree[rt].Min = tmp1 ;
            tree[rt].Max = tmp2 ;
            return ;
        }
        else
        {
            cov[rt] = tmp1 ;
            tree[rt].Sum = (LONG )( r - l + 1) * tmp1 ;
            tree[rt].Min = tmp1 ;
            tree[rt].Max = tmp1 ;
            lazy [rt] = 0;
            return ;
        }
    }
    LONG tmp1 = (LONG )floor(sqrt( tree[rt].Max )  ) ;
    LONG tmp2 = (LONG )floor(sqrt( tree[rt].Min ) ) ;
    if(tmp1 == tmp2)
    {
        cov[rt] = tmp1 ;
        tree[rt].Sum = (LONG )(r -l + 1) * tmp1 ;
        tree[rt].Min = tmp1 ;
        tree[rt].Max = tmp2 ;
        lazy[rt] = 0 ;
        return ;
    }
    int mid = (r + l ) / 2 ;
    Push_down(l , r , rt ) ;
    if(L <= mid)
    Update( L , R , lson ) ;
    if(R > mid )
    Update( L , R , rson ) ;
    Push_up(rt) ;
    return ;
    }
    int mid = (l +r) / 2;
    Push_down(l , r, rt) ;
    if( L <= mid)
        Update(L , R ,lson );
    if( R > mid)
        Update( L , R , rson );
    Push_up(rt) ;
}
LONG Query( int L , int R , int l , int r , int rt)
{
    if( L <= l && r <= R)
    {
        return tree[rt].Sum ;
    }
    int mid = (l +r) /2 ;
    Push_down(l , r ,rt ) ;
    LONG res = 0 ;
    if(L <= mid)
        res += Query( L , R , lson );
    if( R > mid)
        res += Query( L , R , rson ) ;
    return  res ;
}
int main()
{
//    freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","r",stdin);
//     freopen("C:\\Users\\ZhangYuyang\\Desktop\\out2.txt","w",stdout);
    int T;
    cin>>T;
    while(T--)
    {
        int n , m;
        cin>>n>>m;
        build_tree(root) ;
        int  op ;
        int l , r , x;
        for(int i = 1; i<= m ;++i )
        {
            scanf("%d",&op) ;
            if(op == 1)
            {
                scanf("%d%d%d",&l,&r,&x) ;
                Add(l , r , root ,(LONG) x) ;
            }
            else if(op == 2)
            {
                scanf("%d%d",&l,&r);
                Update( l ,r  , root ) ;
            }
            else
            {
                scanf("%d%d" ,&l,&r ) ;
                printf("%lld\n", Query( l, r , root ) ) ;
            }
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值