BZOJ_3038_上帝造题的七分钟2_线段树

BZOJ_3038_上帝造题的七分钟2_线段树

题意:

XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。

 

分析:

在2^64-1以内每个数最多被开方6次就会变成1,或者这个数一开始就是零。

用线段树维护区间和,再记录下区间里的数有没有全都变成1或0。

 

代码:

 

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
#define N 262145
#define LL long long
#define BUG puts("Fuck")
int ls[N],rs[N],n,m,cnt;
LL t[N],len[N];
void bt(int l,int r,int &p){
    if(!p)p=++cnt;
    if(l==r){
        scanf("%lld",&t[p]);
        if(t[p])len[p]=1;return ;
    }
    int mid=l+r>>1;
    bt(l,mid,ls[p]);bt(mid+1,r,rs[p]);
    t[p]=t[ls[p]]+t[rs[p]];
    len[p]=len[ls[p]]+len[rs[p]];
}
void up(int l,int r,int x,int y,int p){
    if(t[p]==len[p])return ;
    if(l==r){t[p]=(LL)sqrt(t[p]);return ;}
    int mid=l+r>>1;
    if(x<=mid)up(l,mid,x,y,ls[p]);
    if(y>mid)up(mid+1,r,x,y,rs[p]);
    t[p]=t[ls[p]]+t[rs[p]];
}
LL query(int l,int r,int x,int y,int p){
    if(x<=l&&r<=y)return t[p];
    int mid=l+r>>1;
    LL re=0;
    if(x<=mid)re+=query(l,mid,x,y,ls[p]);
    if(y>mid)re+=query(mid+1,r,x,y,rs[p]);
    return re;
}
int main(){
    scanf("%d",&n);
    int p=0;
    bt(1,n,p);
    scanf("%d",&m);
    int opt,x,y;
    while(m--){
        scanf("%d%d%d",&opt,&x,&y);
        if(x>y)swap(x,y);
        if(opt==0){
            p=1;
            up(1,n,x,y,p);
        }else{
            p=1;
            printf("%lld\n",query(1,n,x,y,p));
        }
    }
}
/*
10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8
*/

 

posted @ 2018-02-25 16:33  fcwww  阅读(126)  评论(0编辑  收藏  举报