About 分块
分块的的复杂度是带根号的。。。。
然后,它是一种暴力算法
简单来说就是优化过的暴力
分块算法会对一个序列(长度为N)进行划分,每一块最多有K个元素,这样就会分为N/K块;
一般K取sqrt(N),那么块数也有K+1 or K块
通常实现时,我们用belong【i】示第i个位置所属的块,对于每个块都进行信息维护。
单点修改时,我们一般先将对应块的标记下传,再暴力更新被修改块的状态
如果是区间 l,r 修改的话,对于被整块跨过的块直接打标记,两端剩余的部分暴力重构
至于询问操作,和区间修改类似,对于中间跨过的整块,直接利用块保存的信息统计答案,两端剩余部分打暴力
m次询问复杂度o(sqrt(N))
以上就是思路;
建块:
void divide() { //n 个数 int s=sqrt(n);//分成块的大小为sqrt(n); for(int i=1;i<=n;i++) belong[i]=(i-1)/s+1;//belong[i]数组表示第i个数所在的块 int cnt=belong[n];//一共有几块 for(int i=1;i<=cnt;i++) { //设置每一块长度 sum[i]=add[i]=0;//sum表示此时第i块的总值为几,add表示第i块添加的数的总和 if(i==cnt) { //防止最后一块不足s,size表示每一块的长度 if (n%s==0) size[i]=s; else size[i]=n%s; } else size[i]=s; } for(int i=1;i<=n;i++) sum[belong[i]]+=a[i];//i所属的那一块的sum加上ai }
修改:
void modify(int l,int r,int v) { //修改 if (belong[l]==belong[r]) { //属于同一分块 for (int i=l; i<=r; i++)so easy a[i]+=v; return; } while (belong[l]==belong[l-1]) { //自此往下同上访问 a[l]+=v; l++; } while (belong[r]==belong[r+1]) { a[r]+=v; r--; } for (int i=belong[l]; i<=belong[r]; i++) { add[i]+=v; sum[i]+=size[i]*v; } }
查询:
int query(int l,int r) { //访问 int ans=0; if(belong[l]==belong[r]) ans-=sum[belong[l]];//此处为避免l与r属于同一块画图很好理解,此处不再解释 while (belong[l]==belong[l-1]) { ans+=a[l]+add[belong[l]];//因为l要++,所以l会遍历在从l到r的不整区间中所有的数,so要加a数组 //**从左到第一个整区间 l++; } while(belong[r]==belong[r+1]) { //**从最后一个整区间到最后 ans+=a[r]+add[belong[r]]; r--; } for (int i=belong[l]; i<=belong[r]; i++) //加入所有整区间的数 ans+=sum[i]; return ans; }