算法就是将n个数序列分成块k块,每次操作时可以一次操作一块,不需要暴力挨个操作,这是分块的主要思想。
修改时,可以给每个块设置一个加法标记(就是记录这个块中元素加了多少),每次操作对每个整块直接标记,而不完整的块由于元素比较少,可以暴力修改元素的值。
查询时,何求该差不多,在块上的元素直接乘上,不完整的块暴力。
修改:
1 void modify(int a,int b,int c) 2 { 3 4 for(int i=a;i<=min(bl[a]*blo,b);i++) 5 { 6 v[i]+=c; 7 sum[bl[a]]+=c; 8 } 9 if(bl[a] != bl[b]) 10 for(int i=(bl[b]-1)*blo+1;i<=b;i++) 11 { 12 v[i] += c; 13 sum[bl[b]] += c; 14 } 15 for(int i=bl[a]+1;i<=bl[b]-1;i++) 16 atag[i] += c; 17 }
查询区间的和:
1 int query(int a,int b) 2 { 3 int ans=0; 4 for(int i=a;i<=min(bl[a]*blo,b);i++) 5 ans += v[i]+add[bl[a]]; 6 if(bl[a] != bl[b]) 7 for(int i=(bl[b]-1)*blo+1;i<=b;i++) 8 ans += v[i]+atag[bl[b]]; 9 for(int i=bl[a]+1;i<=bl[b]-1;i++) 10 ans += sum[i]+blo*add[i]; 11 return ans; 12 }
合起来就这样了(模板)
1 /*-------------------------分块*/ 2 const int N = 10010; 3 int n,blo; //block块 4 int bl[N]; //快 5 int v[N],add[N],sum[N]; //v数字,add增加值,sum和 6 7 void modify(int a,int b,int c) 8 { 9 10 for(int i=a;i<=min(bl[a]*blo,b);i++) 11 { 12 v[i]+=c; 13 sum[bl[a]]+=c; 14 } 15 if(bl[a] != bl[b]) 16 for(int i=(bl[b]-1)*blo+1;i<=b;i++) 17 { 18 v[i] += c; 19 sum[bl[b]] += c; 20 } 21 for(int i=bl[a]+1;i<=bl[b]-1;i++) 22 atag[i] += c; 23 } 24 25 int query(int a,int b) 26 { 27 int ans=0; 28 for(int i=a;i<=min(bl[a]*blo,b);i++) 29 ans += v[i]+add[bl[a]]; 30 if(bl[a] != bl[b]) 31 for(int i=(bl[b]-1)*blo+1;i<=b;i++) 32 ans += v[i]+atag[bl[b]]; 33 for(int i=bl[a]+1;i<=bl[b]-1;i++) 34 ans += sum[i]+blo*add[i]; 35 return ans; 36 } 37 38 int main() 39 { 40 /*------------预处理---*/ 41 scanf("%d",&n); 42 blo = sqrt(n); 43 for(int i=1; i<=n; i++) 44 scanf("%d",&v[i]); 45 for(int i=1; i<=n; i++) 46 { 47 bl[i] = (i-1)/blo+1; //i点属于bl[i]块 48 sum[bl[i]] += v[i]; //每一块的总和 49 } 50 //... 51 return 0; 52 }