分块
懒啊 引入老朋友博客
分块 根号n
分块,又被称为优雅的暴力,在想不出更优解,可以考虑分块来解决
将大小为n的数列分为根号n块,如果不能恰好分为根号n块的话,多分一块就可以了
所以,我们要预处理出每一个元素所属的块 kuai[i] 和每一块的总值 sum[i]
修改
在每次的区间修改时(单点修改也可以看做区间修改),必定会分出一部分块来
若区间两端点全在一个块内,直接暴力修改 a[i] 就可以了,同时将改区间的总值 sum[i] 做出改变;
若区间两端点不在一个块内,则必定出现整块,左碎块和右碎块(整块:即完整的块,左碎块:即包括左端点在内的小块,从左端点向右到第一个整块的左边界-1,右碎块:即包括右端点在内的小块,从右端点向左到第一个整块的右边界);
左右碎块的处理很简单,暴力修改就可以了,反正绝对不大于 2*根号n;
整块的处理就是块内的每个元素的值 a[i] 不做处理,直接对 sum[i] 动刀,即加上 修改值x*根号n;
再加一个 lazy[i] 数组,记录第i块内的元素整体做了哪些修改,即 lazy[i]+=x;
查询
仍是分出一部分块
若区间两端点在同一块里,暴力查询,每一个的值为 a[i]+lazy[kuai[i]]
若不在,仍分为左右碎块和整块
左右碎块暴力查询,每一个的值为 a[i]+lazy[kuai[i]]
对于整块,直接加上 sum[kuai[i]] ;
上代码
1 ll n,sq,num; 2 ll a[N],kuai[N]; 3 ll lazy[500],sum[500]; 4 //预处理 5 sq=sqrt(n);num++; 6 for(ll i=1;i<=n;++i) 7 { 8 if(num*sq>i)num++; 9 kuai[i]=num; 10 } 11 //修改 12 void update(ll l,ll r,ll k) 13 { 14 if(kuai[l]==kuai[r])//左右端点在一个块内 15 { 16 for(ll i=l;i<=r;++i) 17 { 18 a[i]+=k;sum[kuai[i]]+=k; 19 } 20 return; 21 } 22 for(ll i=l;i<=kuai[l]*sq;++i)//左碎块 23 { 24 a[i]+=k;sum[kuai[i]]+=k; 25 } 26 for(ll i=(kuai[r]-1)*sq+1;i<=r;++i)//右碎块 27 { 28 a[i]+=k;sum[kuai[i]]+=k; 29 } 30 for(ll i=kuai[l]+1;i<=kuai[r]-1;++i)//整块 31 { 32 sum[i]=sum[i]+k*sq;lazy[i]+=k; 33 } 34 } 35 //查询 36 ll ask(ll l,ll r) 37 { 38 ll ans=0; 39 if(kuai[l]==kuai[r])//左右端点在一个块内 40 { 41 for(ll i=l;i<=r;++i) 42 ans=ans+a[i]+lazy[kuai[i]]; 43 return ans; 44 } 45 for(ll i=l;i<=kuai[l]*sq;++i)//左碎块 46 ans=ans+a[i]+lazy[kuai[i]]; 47 for(ll i=(kuai[r]-1)*sq+1;i<=r;++i)//右碎块 48 ans=ans+a[i]+lazy[kuai[i]]; 49 for(ll i=kuai[l]+1;i<=kuai[r]-1;++i)//整块 50 ans=ans+sum[i]; 51 return ans; 52 }