分块入门
基本思想
把一个需要操作的序列分成若干块,分别处理,从而优化时间复杂度。
容易证明块长为
分块常规单次操作复杂度为
接下来给几道例题。
T1
给出一个长为
就当模板放了。
点击查看代码
void add(int l,int r,int c)
{
int sid=id[l],eid=id[r];
if(sid==eid)
{
for(int i=l;i<=r;i++)
a[i]+=c;
return ;
}
for(int i=l;id[i]==sid;i++)a[i]+=c;
for(int i=sid+1;i<eid;i++)tag[i]+=c;
for(int i=r;id[i]==eid;i--)a[i]+=c;
}
T2
给出一个长为
对于区间加法,类似线段树设置懒标记,对于查询操作,可以用二分解决。我们每次区间加法之后对所有涉及到的不完整区间重新排序,保证可以二分找到给定值,对于完整区间,由于顺序不变,则更新
具体的,我们对于每一个块,用一个
放一下查询和更新的代码:
点击查看代码
void work(int x)
{
v[id[x]].clear();
for(int i=(id[x]-1)*len+1;i<=min(n,id[x]*len);i++)
v[id[x]].push_back(a[i]);
sort(v[id[x]].begin(),v[id[x]].end());
}
int ask(int l,int r,int c)
{
int sid=id[l],eid=id[r],ans=0;
if(sid==eid)
{
for(int i=l;i<=r;i++)if(a[i]+tag[id[l]]<c)ans++;
return ans;
}
for(int i=l;id[i]==sid;i++)if(a[i]+tag[id[l]]<c)ans++;
for(int i=sid+1;i<eid;i++)ans+=lower_bound(v[i].begin(),v[i].end(),c-tag[i])-v[i].begin();
for(int i=r;id[i]==eid;i--)if(a[i]+tag[id[r]]<c)ans++;
return ans;
}
T3
给出一个长为
和上一道题很类似,只是对于查询操作,我们二分找到每个块里面符合条件的最大值,取总最大值即可。
查询部分:
点击查看代码
int sid=id[l],eid=id[r],ans=-1;
if(sid==eid)
{
for(int i=l;i<=r;i++)if(a[i]+tag[id[l]]<c)ans=max(ans,a[i]+tag[id[l]]);
return ans;
}
for(int i=l;id[i]==sid;i++)if(a[i]+tag[id[l]]<c)ans=max(ans,a[i]+tag[id[l]]);
for(int i=sid+1;i<eid;i++)
{
int x=c-tag[i];
int y=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
if(--y>=0)
ans=max(ans,v[i][y]+tag[i]);
}
for(int i=r;id[i]==eid;i--)if(a[i]+tag[id[r]]<c)ans=max(ans,a[i]+tag[id[r]]);
return ans;
T4
给出一个长为
简单,模拟线段树的思路,记录区间懒标记
太简单,代码不放了。
T5
给出一个长为
势能分析,由于
整块修改部分代码:
点击查看代码
for(int i=sid+1;i<eid;i++)
if(!f[i])
{
f[i]=1;
for(int j=(i-1)*len+1;j<=min(n,i*len);j++)
{
tag[i]=tag[i]-a[j]+sqrt(a[j]);
a[j]=sqrt(a[j]);
if(a[j]!=0&&a[j]!=1)
f[i]=0;
}
}
T6
给出一个长为
其实稍微想一想就发现,我们只需要维护每一个块的内部排列即可,对于每个修改操作,在块内正常暴力修改,对于每个查询操作,从
放一下代码:
点击查看代码
void add(int x,int k)
{
int sum=0;
for(int i=1;i;i++)
{
if(x<v[i].size())
{
v[i].push_back(maxn);
for(int j=v[i].size()-1;j>x;j--)
{
v[i][j]=v[i][j-1];
}
v[i][x]=k;
return ;
}
x-=v[i].size();
}
}
void ask(int x)
{
int sum=0;
for(int i=1;;i++)
{
if(x<v[i].size()) {cout<<v[i][x]<<'\n';return ;}
x-=v[i].size();
}
}
T7
给出一个长为
左转线段树,模拟懒标记就行了。有点难写,所以代码不放了。
T8
给出一个长为
我们对每个区间记录一个
放一下区间修改的代码:
点击查看代码
for(int i=sid+1;i<eid;i++)
if(tag[i]!=-1)
{
if(tag[i]!=c)tag[i]=c;
else ans+=len;
}
else
{
for(int j=(i-1)*len+1;j<=i*len;j++)
{
if(a[j]!=c)a[j]=c;
else ans++;
}
tag[i]=c;
}
分块是一种很有用的算法,希望大家勤加练习。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】