分块

懒啊   引入老朋友博客

分块 - 云山千叠 - 博客园 (cnblogs.com)

分块  根号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 }
View Code

 

posted @ 2021-11-19 17:49  yfmd  阅读(109)  评论(0编辑  收藏  举报