BZOJ_3038_上帝造题的七分钟2_线段树
题意:
XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。
分析:
在2^64-1以内每个数最多被开方6次就会变成1,或者这个数一开始就是零。
用线段树维护区间和,再记录下区间里的数有没有变成1。
代码:
#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 */