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

时间:2023-03-09 10:01:29
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
*/