数列分块入门 5
思路:因为他的范围在2的32次方之内,所以一个数最多开方5次就会变成1。我们先分块,记录每一块中所有数之和,如果一个块中所有值都为1后,我们就不再对其操作
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstring> #include <stdio.h> #include <algorithm> #include <map> #include <queue> #include <set> #include <sstream> #include <vector> #include <cmath> #include <stack> #include <random> using namespace std; #define io ios::sync_with_stdio(0),cin.tie(0) #define ms(arr) memset(arr,0,sizeof(arr)) #define LD long double #define LL long long #define PI acos(-1.0) #define INF 0x3f3f3f3f #define inf 1<<30 #define ull unsigned long long const int Mod = 998244353; const int maxn = 1e6 + 5; int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n, opt, l, r, c, block; LL a[maxn]; int bl[maxn]; LL sum[maxn]; LL change[maxn]; void ext(int x,int y) { for(int i=x;i<=min(block*bl[x],y);i++){//对左边块开方 sum[bl[i]]-=a[i]; a[i]=sqrt(a[i]); sum[bl[i]]+=a[i]; } if(bl[x]!=bl[y]){//不在同一块 for(int i=block*(bl[y]-1)+1;i<=min(block*bl[y],y);i++){//对最右边块开方 sum[bl[i]]-=a[i]; a[i]=sqrt(a[i]); sum[bl[i]]+=a[i]; } for(int i=bl[x]+1;i<bl[y];i++){ if(change[i]==0){//如果当前块中所有的值不都为一 change[i]=1; for(int j=block*(i-1)+1;j<=min(block*i,n);j++){//依次开方 sum[i]-=a[j]; a[j]=sqrt(a[j]); sum[i]+=a[j]; if(a[j]>1) change[i]=0; } } } } } void query(int x,int y) { LL ans=0; for(int i=x;i<=min(block*bl[x],y);i++){//加上左边块 ans=ans+a[i]; } if(bl[x]!=bl[y]){//不在同一块 for(int i=block*(bl[y]-1)+1;i<=min(block*bl[y],y);i++){//加上右边块 ans=ans+a[i]; } for(int i=bl[x]+1;i<bl[y];i++){//加上中间块 ans=ans+sum[i]; } } printf("%lld\n",ans); } int main() { io; n = read(); block=sqrt(n); for (int i = 1; i <= n; i++) { a[i] = read(); } for(int i=1;i<=n;i++){//分块操作 bl[i]=(i-1)/block+1; sum[bl[i]]+=a[i];//记录当前块的总和 } for(int i=1;i<=n;i++){ opt=read();l=read();r=read();c=read(); if(opt==0)//开方 ext(l,r); else query(l,r); } return 0; }