考虑用线段树进行区间求和,暴力修改。
我们发现开根号操作并不能直接像加减一样直接修改,但是他有一个非常良好的性质,对于一个小于等于1e12的数,其进行下取整开根操作至多不超过6次就可以到1,对于1,我们显然不需要进行任何操作,因此修改的复杂度是O(nlogn),数据只有1e5,因此不用在意常数。而线段树直接区间求和的复杂度也是O(nlogn),所以总时间复杂度为O(小常数*nlogn)
1 #pragma GCC optimize(3,"Ofast","inline") 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 #define N 100005 8 #define int long long 9 using namespace std; 10 int read() 11 { 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} 15 return x*f; 16 } 17 int n,m,a[N]; 18 struct node 19 { 20 int l,r,sum,maxn; 21 }tree[N*4]; 22 void pushup(int k) 23 { 24 tree[k].maxn=max(tree[k*2].maxn,tree[k*2+1].maxn); 25 tree[k].sum=tree[k*2].sum+tree[k*2+1].sum; 26 } 27 void build(int l,int r,int k) 28 { 29 tree[k].l=l;tree[k].r=r; 30 if(l==r) 31 { 32 tree[k].sum=tree[k].maxn=a[l]; 33 return; 34 } 35 int mid=l+r>>1; 36 build(l,mid,k*2); 37 build(mid+1,r,k*2+1); 38 pushup(k); 39 } 40 void modify(int x,int y,int k) 41 { 42 int l=tree[k].l,r=tree[k].r; 43 if(l==r) 44 { 45 tree[k].sum=sqrt(tree[k].sum); 46 tree[k].maxn=sqrt(tree[k].maxn); 47 return; 48 } 49 int mid=l+r>>1; 50 if(x<=mid&&tree[k*2].maxn>1)modify(x,y,k*2); 51 if(y>mid&&tree[k*2+1].maxn>1)modify(x,y,k*2+1); 52 pushup(k); 53 } 54 int query(int x,int y,int k) 55 { 56 int l=tree[k].l,r=tree[k].r; 57 if(l>=x&&r<=y)return tree[k].sum; 58 int mid=l+r>>1,res=0; 59 if(x<=mid)res+=query(x,y,k*2); 60 if(y>mid)res+=query(x,y,k*2+1); 61 return res; 62 } 63 signed main() 64 { 65 freopen("god.in","r",stdin); 66 freopen("god.out","w",stdout); 67 n=read(); 68 for(int i=1;i<=n;i++)a[i]=read(); 69 build(1,n,1); 70 m=read(); 71 int opt,x,y; 72 while(m--) 73 { 74 opt=read();x=read();y=read(); 75 if(x>y)swap(x,y); 76 if(opt==2)modify(x,y,1); 77 else printf("%lld ",query(x,y,1)); 78 } 79 return 0; 80 }