HDU 4027 Can you answer these queries【线段树】

<题目链接>

题目大意:

给定一段序列,现在对指定区间进行两种操作:一是对指定区间进行修改,对其中的每个数字都开根号(开根号后的数字仍然取整);二是对指定区间进行查询,查询这段区间所有数字的和。

解题分析:

本题虽然是区间修改,但是不需要用 lazy标记,因为要对指定区间的每个数进行开根号的处理,也就是说,每次 update ,都要延伸到该区间涉及到的叶子节点,进行开根,而不是在叶子节点上端的某个节点就将开根的指令存储下来。那么是不是说我们每次只能对 update 的每个区间所涉及到的每个节点进行暴力的单点修改呢?很显然不是的,因为每个节点的值不超过2^63,所以每个值的有效开方次数并不多。所以我们对线段树的每个节点引入一个标记cnt,用它来记录该节点对应的区域是否全部不需要开方,如果不需要开方,那么就直接return ,终止无效更新,从而提高效率。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

#define Lson rt<<1,l,mid
#define Rson rt<<1|1,mid+1,r
const int M=100000+5;
typedef long long ll;
ll n,m,arr[M];

struct Tree{
    ll sum,cnt;    //cnt标记该区间是否需要继续向下开根
}tr[M<<2];

void Pushup(int rt){
    if(tr[rt<<1].cnt==0&&tr[rt<<1|1].cnt==0)tr[rt].cnt=0;    //如果这一整个区间全部不用开根号,则cnt置为0
    else tr[rt].cnt=1;
    tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
}

void build(int rt,int l,int r){
    tr[rt].cnt=1;    //cnt=1代表这段区间需要开根号 
    if(l==r){
        tr[rt].sum=arr[l];
        return;
    }
    int mid=(l+r)>>1;
    build(Lson);
    build(Rson);
    Pushup(rt);
}

void update(int rt,int l,int r,int L,int R){
    if(tr[rt].cnt==0)return;     //如果rt节点所对应的区间全部不需要再开根,就直接return,不需要继续向下查询
    if(l==r){
        tr[rt].sum=(int)sqrt(tr[rt].sum*1.0);
        if(tr[rt].sum==1)tr[rt].cnt=0;  //如果这个叶子节点的值变为1,那么以后就可以不用开根了,因为1开根号还是1
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)update(Lson,L,R);
    if(R>mid)update(Rson,L,R);
    Pushup(rt);
}

ll query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return tr[rt].sum;
    }
    int mid=(l+r)>>1;
    ll ans=0;
    if(L<=mid)ans+=query(Lson,L,R);
    if(R>mid)ans+=query(Rson,L,R);
    return ans;
}

int main(){
    int ncase=0;
    while(scanf("%lld",&n)!=EOF){
        for(int i=1;i<=n;i++){
            scanf("%lld",&arr[i]);
        }
        build(1,1,n);
        scanf("%lld",&m);
        printf("Case #%d:\n",++ncase);
        for(int i=1;i<=m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(b>c)swap(b,c);    //这里一定要注意,如果输入的b>c,则应该交换一下位置
            if(!a)update(1,1,n,b,c);
            else{
                printf("%lld\n",query(1,1,n,b,c));
            }
        }
        printf("\n");
    }
    return 0;
}

2018-09-23

猜你喜欢

转载自www.cnblogs.com/00isok/p/9693144.html