分块 note
Rmtsaikoda · · 个人记录
有一说一,分块写得太爽了
三倍经验:\ P3870\ P2864\ P2574
len=sqrt(n);
for(int i=1;i<=n;i++)
id[i]=(i-1)/len+1;
这一步对数组划分的处理很精妙\ 后面的分区间暴力处理听着慢,写着快\ 整块快速溜,散块暴力处理\
void change(int l,int r){
int st=id[l],ed=id[r],js=0;
if(st==ed){
for(int i=l;i<=r;i++){
if(a[i]^b[st])js++;
a[i]=a[i]^1;
}
s[st]+=(r-l+1-2*js);
}else{
for(int i=l;id[i]==st;i++){
if(a[i]^b[st])js++;
a[i]=a[i]^1;
}
s[st]+=(st*len-l+1-2*js);js=0;
for(int i=st+1;i<ed;i++)b[i]=b[i]^1,s[i]=len-s[i];
for(int i=r;id[i]==ed;i--){
if(a[i]^b[ed])js++;
a[i]=a[i]^1;
}
s[ed]+=(r-(ed-1)*len-2*js);
}
}
这个是区间修改,这里懒标记永久化,且对散块的区间整体修改也不在循环中进行,两者大大加快了速度。\
简述原理:\
因为是对区间修,所以有关联性,如st=ed时,本区间内的
s[st]+=(r-l+1-2*js);
记得因为懒标记未下传,而
if(a[i]^b[id[i]])js++;
整块的处理上因为单块中
s[i]=len-s[i];
最后求和答案
int query(int l,int r){
int st=id[l],ed=id[r],ans=0;
if(st==ed){
for(int i=l;i<=r;i++)ans=ans+(a[i]^b[st]);
}else{
for(int i=l;id[i]==st;i++)ans=ans+(a[i]^b[st]);
for(int i=st+1;i<ed;i++)ans=ans+s[i];
for(int i=r;id[i]==ed;i--)ans=ans+(a[i]^b[ed]);
}
return ans;
}
记得下传懒标记
#
送个板子题P2357
附送代码,同样做了
#include<bits/stdc++.h>
using namespace std;
long long n,q,id[200005],len,s[2005],a[200005],b[200005];
void change(int l,int r,long long k){
int st=id[l],ed=id[r];
long long ans=0;
if(st==ed){
for(int i=l;i<=r;i++)a[i]+=k;
s[st]+=(r-l+1)*k;
}else{
for(int i=l;id[i]==st;i++)a[i]+=k;
s[st]+=(st*len-l+1)*k;
for(int i=st+1;i<ed;i++)b[i]+=k,s[i]+=len*k;
for(int i=r;id[i]==ed;i--)a[i]+=k;
s[ed]+=(r-(ed-1)*len)*k;
}
}
long long query(int l,int r){
int st=id[l],ed=id[r];
long long ans=0;
if(st==ed)
for(int i=l;i<=r;i++)ans+=(a[i]+b[st]);
else{
for(int i=l;id[i]==st;i++)ans+=(a[i]+b[st]);
for(int i=st+1;i<ed;i++)ans+=s[i];
for(int i=r;id[i]==ed;i--)ans+=(a[i]+b[ed]);
}
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>q;
len=sqrt(n);
for(int i=1;i<=n;i++){
cin>>a[i];
id[i]=(i-1)/len+1;
s[id[i]]+=a[i];
}
for(long long i=1,x,l,r,k;i<=q;i++){
cin>>x;
if(x==1){
cin>>l>>r>>k;
change(l,r,k);
}else if(x==2){
cin>>k;
change(1,1,k);
}else if(x==3){
cin>>k;
change(1,1,-k);
}else if(x==4){
cin>>l>>r;
cout<<query(l,r)<<'\n';
}else cout<<(a[1]+b[1])<<'\n';
}
return 0;
}
建议先学线段树再来碰分块(有些线段树题可以用分块来做),便于理解懒标记