题目大意是一个长度为 N (N<=1e5)的区间,区间里元素都是正整数,现在有M(M<=1e5)次区间修改与查询,区间修改的操作L,R会将[L,R]区间里面的所有元素开方(向下取整),区间查询会询问[L,R]的区间和,保证区间和小于 2^63,多组样例输入。
开始拿到这个题思路有些停滞,因为这个修改区间里每个元素的修改是不同的,而查询又是区间和,每个数字被开方到1的次数是很少的所以想从lazy标记下手存区间修改次数,查询时下推但又担心复杂度。
后来再发现通过剪枝可以很好解决复杂度的问题,就算单个元素是 2^64 大小,其被开方到1也只用6次,开始进行暴力的修改叶结点,如果在修改时发现区间和全为1就不需要再往下搜索,这样叶结点最多被访问6次,其它结点被访问最糟糕也是在单点修改的情况下:最多十七层( 0<=i <17,17为logN)而每层结点被访问的次数和都是 6*(2^i)*(N/(2^i)) ,何况单个元素小于 2^63 ,所以修改的总开销是 O(constant*NlogN),而查询仍是一次O(logN),也不会超过M次,复杂度OK。
1 #pragma optimize("O3","unroll-loops") 2 #pragma target("avx3") 3 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define Lson(x) ((x)<<1) 7 #define Rson(x) ((x)<<1|1) 8 typedef long long ll; 9 const int maxn = 1e5; 10 ll raw[maxn+10]; 11 ll d[(maxn<<2)+10]; 12 13 void build(int s,int t,int p){ 14 if(s == t){ 15 d[p] = raw[s]; 16 return; 17 } 18 int mid = (s + t) >> 1; 19 build(s,mid,Lson(p)); 20 build(mid+1,t,Rson(p)); 21 d[p] = d[Lson(p)] + d[Rson(p)]; 22 } 23 24 void update(int L,int R,int s,int t,int p){ 25 // printf("%d %d %d %d ",L,R,s,t); 26 if(d[p] <= 1ll*(t-s+1)) 27 return ; 28 if(L == s && t == R && d[p] == (t-s+1)) 29 return ; 30 if(s == t){ 31 d[p] = sqrt(d[p]); 32 return ; 33 } 34 int mid = (s + t) >> 1; 35 if(R<=mid) update(L,R,s,mid,Lson(p)); 36 else if(L > mid) update(L,R,mid+1,t,Rson(p)); 37 else update(L,mid,s,mid,Lson(p)) , update(mid+1,R,mid+1,t,Rson(p)); 38 d[p] = d[Lson(p)] + d[Rson(p)]; 39 } 40 41 ll query(int L,int R,int s,int t,int p){ 42 if(L == s && t == R){ 43 return d[p]; 44 } 45 int mid = (s + t) >> 1; 46 ll sum = 0; 47 if(R<=mid) sum += query(L,R,s,mid,Lson(p)); 48 else if(L > mid) sum += query(L,R,mid+1,t,Rson(p)); 49 else sum = query(L,mid,s,mid,Lson(p)) + query(mid+1,R,mid+1,t,Rson(p)); 50 return sum; 51 } 52 53 int main(){ 54 // __clock_t stt = clock(); 55 int cntcase =0 ; 56 int N; 57 while(scanf("%d",&N)==1){ 58 for(int i=1;i<=N;++i){ 59 scanf("%lld",&raw[i]); 60 } 61 build(1,N,1); 62 63 int M; 64 scanf("%d",&M); 65 cntcase ++; 66 printf("Case #%d: ",cntcase); 67 for(int i=0;i<M;++i){ 68 int op,L,R; 69 scanf("%d%d%d",&op,&L,&R); 70 if(L>R) swap(L,R); 71 if(op) 72 printf("%lld ",query(L,R,1,N,1)); 73 else 74 update(L,R,1,N,1); 75 // for(int j=1;j<=N;++j) 76 // printf("%lld%c",query(j,j,1,N,1),j==N?' ':' '); 77 } 78 printf(" "); 79 } 80 // __clock_t edt = clock(); 81 // printf("Used Time : %.3lfms ", static_cast<double>(edt - stt)/1000); 82 return 0; 83 }
这题坑的地方还有一个,就是给出的区间并不是 L <= R 的需要判断一下swap ,因为这个TLE了好几次,还怀疑自己复杂度出问题了用随机大数据测了好几回,最后才发现题目Input里没做L,R的大小限制。通过这道题倒是让我觉得线段树和搜索在本质上有一定的相似。