【题目大意】
给出一些数,有两种操作。(1)将区间内每一个数开方(2)查询每一段区间的和
【思路】
普通的线段树保留修改+开方优化。可以知道当一个数为0或1时,无论开方几次,答案仍然相同。所以设置flag=1。如果一个节点的左右孩子flag均为1,那么它的flag也是1。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 #define LL long long 9 using namespace std; 10 const int MAXN=100000+500; 11 int n,m; 12 LL sum[MAXN<<2]; 13 int flag[MAXN<<2]; 14 15 void pushup(int rt) 16 { 17 sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 18 flag[rt]=flag[rt<<1]&flag[rt<<1|1]; 19 } 20 21 void build(int l,int r,int rt) 22 { 23 flag[rt]=0; 24 if (l==r) 25 { 26 scanf("%lld",&sum[rt]); 27 return; 28 } 29 int m=(l+r)>>1; 30 build(lson); 31 build(rson); 32 pushup(rt); 33 } 34 35 void update(int L,int R,int l,int r,int rt) 36 { 37 if (flag[rt]) return; 38 if (l==r) 39 { 40 sum[rt]=(LL)sqrt(sum[rt]); 41 if (sum[rt]==0 || sum[rt]==1) flag[rt]=1; 42 return; 43 } 44 int m=(l+r)>>1; 45 if (L<=m) update(L,R,lson); 46 if (R>m) update(L,R,rson); 47 pushup(rt); 48 } 49 50 LL query(int L,int R,int l,int r,int rt) 51 { 52 if (L<=l && r<=R) return sum[rt]; 53 int m=(l+r)>>1; 54 LL ret=0; 55 if (L<=m) ret+=query(L,R,lson); 56 if (R>m) ret+=query(L,R,rson); 57 return ret; 58 } 59 60 int main() 61 { 62 scanf("%d",&n); 63 build(1,n,1); 64 scanf("%d",&m); 65 for (int i=0;i<m;i++) 66 { 67 int x,l,r; 68 scanf("%d%d%d",&x,&l,&r); 69 if (x==1) printf("%lld ",query(l,r,1,n,1)); 70 else update(l,r,1,n,1); 71 } 72 return 0; 73 }