学习笔记——分块
分块
我们先引入一道题:
有一个长度为
用线段树或树状数组能在
预处理
把数列
然后算出每段的前缀和数组
tmp=sqrt(n);
for(int i=1;i<=tmp;i++){
L[i]=(i-1)*sqrt(n)+1;
R[i]=i*sqrt(n);
}
if(R[tmp]<n)tmp++,L[tmp]=R[tmp-1]+1,R[tmp]=n;
for(int i=1;i<=tmp;i++){
for(int j=L[i];j<=R[i];j++){
pos[j]=i;
sum[i]+=a[j];
}
}
区间加法
- 如果
与 同时处于第 段内,则直接将 都加 ,同时改变前缀和数组 。 - 设
在 段, 在 段。那么对于 ,令 。而对于不足一整段的开头和结尾两部分则按照第 种情况更新。
void change(int l,int r,int d){
int p=pos[l],q=pos[r];//pos数组存储每个位置是哪一段
if(p==q){
for(int i=l;i<=r;i++)a[i]+=d;
sum[p]+=d*(r-l+1);
}else{
for(int i=p+1;i<=q-1;i++)add[i]+=d;
for(int i=l;i<=R[p];i++)a[i]+=d;
sum[p]+=d*(R[p]-l+1);
for(int i=L[q];i<=r;i++)a[i]+=d;
sum[q]+=d*(r-L[q]+1);
}
}
区间求和
- 如果
与 同时处于第 段内,则答案就是 。 - 设
在 段, 在 段, 为 。那么对于 ,令 , 表示第 段长度。而对于不足一整段的开头和结尾两部分则按照第 种情况计算。
int ask(int l,int r){
int p=pos[l],q=pos[r];
int ans=0;
if(p==q){
for(int i=l;i<=r;i++)ans+=a[i];
ans+=add[p]*(r-l+1);
}else{
for(int i=p+1;i<=q-1;i++)
ans+=sum[i]+add[i]*(R[i]-L[i]+1);
for(int i=l;i<=R[p];i++)ans+=a[i];
ans+=add[p]*(R[p]-l+1);
for(int i=L[q];i<=r;i++)ans+=a[i];
ans+=add[q]*(r-L[q]+1);
}
return ans;
}
Example
这里以Luogu P2801为例。
分块好题,只不过预处理时需要排序。
#include<bits/stdc++.h>
#define int long long
#define MAX 1000005
using namespace std;
int n,q,a[MAX],b[MAX],tmp,L[MAX],R[MAX],add[MAX],sum[MAX],pos[MAX];
char ch;
void change(int l,int r,int w){
int p=pos[l],q=pos[r];
if(p==q){
for(int i=l;i<=r;i++)a[i]+=w;
for(int i=L[p];i<=R[q];i++)b[i]=a[i];
sort(b+L[p],b+R[q]+1);
}else{
for(int i=p+1;i<=q-1;i++)add[i]+=w;
for(int i=l;i<=R[p];i++)a[i]+=w;
for(int i=L[p];i<=R[p];i++)b[i]=a[i];
sort(b+L[p],b+R[p]+1);
for(int i=L[q];i<=r;i++)a[i]+=w;
for(int i=L[q];i<=R[q];i++)b[i]=a[i];
sort(b+L[q],b+R[q]+1);
}
}
int ask(int l,int r,int w){
int p=pos[l],q=pos[r];
int ans=0,t;
if(p==q){
for(int i=l;i<=r;i++){
if(add[p]+a[i]>=w)ans++;
}
}else{
for(int i=p+1;i<=q-1;i++){
t=lower_bound(b+L[i],b+R[i]+1,w-add[i])-b;
ans+=R[i]-t+1;
}
for(int i=l;i<=R[p];i++){
if(add[p]+a[i]>=w)ans++;
}
for(int i=L[q];i<=r;i++){
if(add[q]+a[i]>=w)ans++;
}
}
return ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i];
tmp=sqrt(n);
for(int i=1;i<=tmp;i++){
L[i]=(i-1)*sqrt(n)+1;
R[i]=i*sqrt(n);
}
if(R[tmp]<n)tmp++,L[tmp]=R[tmp-1]+1,R[tmp]=n;
for(int i=1;i<=tmp;i++){
for(int j=L[i];j<=R[i];j++){
pos[j]=i;
sum[i]+=a[j];
}
}
for(int i=1;i<=tmp;i++){
sort(b+L[i],b+R[i]+1);
}
int l,r,w;
while(q--){
cin>>ch>>l>>r>>w;
if(ch=='M')change(l,r,w);
else cout<<ask(l,r,w)<<'\n';
}
return 0;
}
分类:
学习笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探