数据结构:分块-区间开方与区间求和
在对整块儿进行开方操作的时候,因为块儿内每一个元素都有各自的特点,所以不好去统一维护
这题的修改就只有下取整开方,而一个数经过几次开方之后,它的值就会变成 0 或者 1
如果每次区间开方只不涉及完整的块,意味着不超过2√n个元素,直接暴力即可
如果涉及了一些完整的块,这些块经过几次操作以后就会都变成 0 / 1
只要每个整块暴力开方后,记录一下元素是否都变成了 0 / 1
区间修改时跳过那些全为 0 / 1 的块即可
这样每个元素至多被开方不超过4次
这种优化就比较不显然了,但是很管用
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 const int maxn=50005; 6 int n,blo; 7 int bl[maxn],v[maxn],sum[maxn],flag[maxn]; 8 long long read() 9 { 10 long long x=0,f=1;char ch=getchar(); 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 13 return x*f; 14 } 15 void solve_sqrt(int x) 16 { 17 if(flag[x]) return; 18 flag[x]=1; 19 sum[x]=0; 20 for(int i=(x-1)*blo+1;i<=x*blo;i++) 21 { 22 v[i]=sqrt(v[i]),sum[x]+=v[i]; 23 if(v[i]>1) flag[x]=0; 24 } 25 } 26 void add(int a,int b,int c) 27 { 28 for(int i=a;i<=min(bl[a]*blo,b);i++) 29 { 30 sum[bl[a]]-=v[i]; 31 v[i]=sqrt(v[i]); 32 sum[bl[a]]+=v[i]; 33 } 34 if(bl[a]!=bl[b]) 35 for(int i=(bl[b]-1)*blo+1;i<=b;i++) 36 { 37 sum[bl[b]]-=v[i]; 38 v[i]=sqrt(v[i]); 39 sum[bl[b]]+=v[i]; 40 } 41 for(int i=bl[a]+1;i<=bl[b]-1;i++) 42 solve_sqrt(i); 43 } 44 int query(int a,int b) 45 { 46 int ans=0; 47 for(int i=a;i<=min(bl[a]*blo,b);i++) ans+=v[i]; 48 if(bl[a]!=bl[b]) 49 for(int i=(bl[b]-1)*blo+1;i<=b;i++) ans+=v[i]; 50 for(int i=bl[a]+1;i<=bl[b]-1;i++) ans+=sum[i]; 51 return ans; 52 } 53 int main() 54 { 55 n=read();blo=sqrt(n); 56 for(int i=1;i<=n;i++) v[i]=read(); 57 for(int i=1;i<=n;i++) 58 { 59 bl[i]=(i-1)/blo+1; 60 sum[bl[i]]+=v[i]; 61 } 62 for(int i=1;i<=n;i++) 63 { 64 int f=read(),a=read(),b=read(),c=read(); 65 if(f==0) add(a,b,c); 66 if(f==1) printf("%d\n",query(a,b)); 67 } 68 }
分块儿不仅简单而且骚操作啊