小小萌新刚开始学分块,还请求分块大人善待(^_-)
分块嘛,总觉得分块代码比SGT(线段树)短,比BIT(树状数组)好理解,但好多大佬都推荐BIT,算了,多学了总比少学好。。。
一号板子题:
先上一道基础题对分块的中心思想加以理解,,,
单点修改&单点查询
(也可以用线段树写)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea=1e5+7;
int n,q,x,y,block,num,s,belong[sea],l[sea],r[sea];
LL maxx[sea],a[sea];
void build()//建块
{
block=sqrt(n); num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;
//(建块板子)
for(int i=1;i<=num;i++)
for(int j=l[i];j<=r[i];j++)
maxx[i]=max(maxx[i],a[j]);
}
void alter(int x,int y)//单点修改
{
a[x]+=y;
maxx[belong[x]]=max(maxx[belong[x]],a[x]);
}
LL ask(int x,int y)//单点查询
{
LL ans=0;
if(belong[x]==belong[y])
{
for(int i=x;i<=y;i++)
ans=max(ans,a[i]);
return ans;
}
for(int i=x;i<=r[belong[x]];i++)
ans=max(ans,a[i]);
for(int i=belong[x]+1;i<belong[y];i++)
ans=max(ans,maxx[i]);
for(int i=l[belong[y]];i<=y;i++)
ans=max(ans,a[i]);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
memset(a,0,sizeof(a));
memset(maxx,0,sizeof(maxx));
build();
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&s,&x,&y);
if(s==1) alter(x,y);
else printf("%lld\n",ask(x,y));
}
return 0;
}
既然下定决心学习分块就少不了hzwer学长的:数列分块入门
(坚持刷完!!!!)
二号板子题:
区间修改&单点查询
(还是推荐LOJ,比较好的一个OJ)
这是一系列的分块练习,认为还是比较全的。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea=1e5+7;
int n,block,belong[sea],num,l[sea],r[sea],Lazy[sea],x,y,z,s;
LL a[sea];
void build()
{
block=sqrt(n); num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;
}
void alter(int x,int y,int z)
{
if(belong[x]==belong[y])
for(int i=x;i<=y;i++) a[i]+=z;
else
{
for(int i=x;i<=r[belong[x]];i++) a[i]+=z;
for(int i=belong[x]+1;i<belong[y];i++) Lazy[i]+=z; //最最最重要的分块代码,傻掉的我竟然写错了,55555,所以菜鸡就是菜鸡~~~
for(int i=l[belong[y]];i<=y;i++) a[i]+=z;
}
}
LL ask(int x)
{
return a[x]+Lazy[belong[x]];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build();
for(int i=1;i<=n;i++)
{
scanf("%d",&s);
if(s==0)
{
scanf("%d%d%d",&x,&y,&z);
alter(x,y,z);
}
else
{
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",ask(y));
}
}
return 0;
}
三号板子题:
区间修改&区间查询
看讨论组里有大佬说这个题暴力O3都能过,数据怎么可以这么水,但是还是要好好练习分块。。。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea=1e5+7;
int n,block,belong[sea],num,l[sea],r[sea],Lazy[sea],x,y,z,s;
LL a[sea];
vector<int>v[sea];//想了很久原来想用sort,然后想用堆优化,看了看大佬们的题解,都是用verctor也就这样用了,堆应该也能写
void reset(int x)//对第x块的处理
{
v[x].clear();
for(int i=(x-1)*block+1;i<=min(x*block,n);i++)
v[x].push_back(a[i]);
sort(v[x].begin(),v[x].end());
}
void alter_ad(int x,int y,LL z)//区间修改
{
for(int i=x;i<=min(belong[x]*block,y);i++) a[i]+=z;
reset(belong[x]);
if(belong[x]!=belong[y])
{
for(int i=block*(belong[y]-1)+1;i<=y;i++) a[i]+=z;
reset(belong[y]);
}
for(int i=belong[x]+1;i<belong[y];i++) Lazy[i]+=z;
}
LL ask(int x,int y,LL z)//区间查询
{
LL ans=0;
for(int i=x;i<=min(belong[x]*block,y);i++)
if(a[i]+Lazy[belong[x]]<z) ans++;
if(belong[x]!=belong[y])
{
for(int i=belong[x]+1;i<belong[y];i++)
{
LL xx=z-Lazy[i];
ans+=lower_bound(v[i].begin(),v[i].end(),xx)-v[i].begin();
}
for(int i=(belong[y]-1)*block+1;i<=y;i++)
if(a[i]+Lazy[belong[y]]<z) ans++;
}
return ans;
}
int main()
{
scanf("%d",&n);
block=0.5*sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
belong[i]=(i-1)/block+1;
v[belong[i]].push_back(a[i]);
}
for(int i=1;i<=belong[n];i++)
sort(v[i].begin(),v[i].end());
for(int i=1;i<=n;i++)
{
scanf("%d",&s);
if(s==0)
{
scanf("%d%d%d",&x,&y,&z);
alter_ad(x,y,z);
}
else
{
scanf("%d%d%d",&x,&y,&z);
printf("%lld\n",ask(x,y,z*z));
}
}
return 0;
}
四号板子题:
区间修改&区间查询(前驱)
当时一看到这道题,还不知道前驱是什么,就找了找hzwer学长的博客,再次强调一下:
前驱:是指在此数之前的小于此数最大的数值
(解法与之前第三题基本一样)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea=1e5+7;
int n,block,belong[sea],num,l[sea],r[sea],Lazy[sea],x,y,z,s;
int a[sea];
vector<int>v[sea];//vector对于排序用的比较方便,但是我个人还是比较喜欢不用它,毕竟STL不是很快
void build()//还是习惯于建块(可能是因为线段树吧)
{
block=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
belong[i]=(i-1)/block+1;
v[belong[i]].push_back(a[i]);
}
for(int i=1;i<=belong[n];i++)
sort(v[i].begin(),v[i].end());
//基本建块+vector的操作
}
void reset(int x)
{
v[x].clear();
for(int i=(x-1)*block+1;i<=min(x*block,n);i++)
v[x].push_back(a[i]);
sort(v[x].begin(),v[x].end());
}
void alter_ad(int x,int y,LL z)
{
for(int i=x;i<=min(belong[x]*block,y);i++) a[i]+=z;
reset(belong[x]);
if(belong[x]!=belong[y])
{
for(int i=block*(belong[y]-1)+1;i<=y;i++) a[i]+=z;
reset(belong[y]);
}
for(int i=belong[x]+1;i<belong[y];i++) Lazy[i]+=z;
}
int ask(int x,int y,LL z)
{
//基础操作和第二题基本相同,就是改一下求前驱即可
int ans=-1;
for(int i=x;i<=min(belong[x]*block,y);i++)
if(a[i]+Lazy[belong[x]]<z) ans=max(ans,a[i]+Lazy[belong[x]]);
if(belong[x]!=belong[y])
{
for(int i=(belong[y]-1)*block+1;i<=y;i++)
if(a[i]+Lazy[belong[y]]<z)
ans=max(ans,a[i]+Lazy[belong[y]]);
}
for(int i=belong[x]+1;i<belong[y];i++)
{
int kk=lower_bound(v[i].begin(),v[i].end(),z-Lazy[i])-v[i].begin();
if(kk==0) continue;
ans=max(ans,v[i][kk-1]+Lazy[i]);
}
return ans;
}
int main()
{
scanf("%d",&n);
build();
for(int i=1;i<=n;i++)
{
scanf("%d",&s);
if(s==0)
{
scanf("%d%d%d",&x,&y,&z);
alter_ad(x,y,z);
}
else
{
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",ask(x,y,z));
}
}
return 0;
}
五号板子题:
区间修改&区间查询(求和取模)
啊啊啊啊啊啊~~~~这么简单的一道题耗费了我一个多小时,二十分钟打出来,用了八九十分钟去调,结果死在了手残上,打算剁手了555555555
#include<bits/stdc++.h>
#define sea 50005
#define LL long long
using namespace std;
int block,num,belong[sea],l[sea],r[sea],n,s,x,y,z;
LL ans=0,a[sea],ins[sea],sum[sea];
void build()
{
block=sqrt(n); num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1,sum[belong[i]]+=a[i];
}
void alter_add(int x,int y,int z)
{
if(belong[x]==belong[y])
for(int i=x;i<=y;i++) a[i]+=z,sum[ins[i]]+=z;
else
{
for(int i=x;i<=r[belong[x]];i++) a[i]+=z,sum[ins[i]]+=z;
for(int i=belong[x]+1;i<belong[y];i++) ins[i]+=z;
for(int i=l[belong[y]];i<=y;i++) a[i]+=z,sum[ins[i]]+=z;
}
}
LL ask(int x,int y,int z)
{
LL ans=0;
if(belong[x]==belong[y])
for(int i=x;i<=y;i++) ans+=a[i]+ins[belong[i]];
else
{
for(int i=x;i<=r[belong[x]];i++) ans+=a[i]+ins[belong[x]];
for(int i=belong[x]+1;i<belong[y];i++) ans+=sum[i]+block*ins[i];
for(int i=l[belong[y]];i<=y;i++) ans+=a[i]+ins[belong[y]];
}
return ans;
}//还是喜欢这种风格(不用verctor),嘻嘻
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build();
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&s,&x,&y,&z);
ans=0;
if(s==0) alter_add(x,y,z);
else printf("%lld\n",ask(x,y,z)%(z+1));
}
return 0;
}
六号板子题:
区间修改&区间查询(开方求和)
比较不好操作的区间修改,刚开始想着直接暴力打吧,但是想了想分块,开方在sum[]数组中不好操作,但是看了hzwer的博客之后,简直是大写的佩服,暴力开方还能写的这么强,重要的是还有优化了开方次数,太强了(膜的是不是有点过0.0)
#include<bits/stdc++.h>
#define sea 50005
#define LL long long
using namespace std;
int block,num,belong[sea],l[sea],r[sea],n,s,x,y,z;
LL ans=0,a[sea],ins[sea],sum[sea];
void build()
{
block=sqrt(n); num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1,sum[belong[i]]+=a[i];
}
void kfbl(int x)//分块暴力(缩写)
{
if(ins[x]) return;
ins[x]=1;//标记数组
sum[x]=0;//整块赋值为0
for(int i=l[x];i<=r[x];i++)
{
a[i]=sqrt(a[i]),sum[x]+=a[i];//对于整块的分解,无限开方,但是如果有1或者0就记录下来每次跳过即可
if(a[i]>1) ins[x]=0;
}
}
void alter_kf(int x,int y)
{
if(belong[x]==belong[y])
for(int i=x;i<=y;i++) sum[belong[x]]-=a[i],a[i]=sqrt(a[i]),sum[belong[x]]+=a[i];//要记得先删除在修改最后添加
else
{
for(int i=x;i<=r[belong[x]];i++) sum[belong[x]]-=a[i],a[i]=sqrt(a[i]),sum[belong[x]]+=a[i];
for(int i=belong[x]+1;i<belong[y];i++) kfbl(i);
for(int i=l[belong[y]];i<=y;i++) sum[belong[y]]-=a[i],a[i]=sqrt(a[i]),sum[belong[y]]+=a[i];
}
}
LL ask(int x,int y)
{
LL ans=0;
if(belong[x]==belong[y])
for(int i=x;i<=y;i++) ans+=a[i];
else
{
for(int i=x;i<=r[belong[x]];i++) ans+=a[i];
for(int i=belong[x]+1;i<belong[y];i++) ans+=sum[i];
for(int i=l[belong[y]];i<=y;i++) ans+=a[i];
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build();
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&s,&x,&y,&z);
ans=0;
if(s==0) alter_kf(x,y);
else printf("%lld\n",ask(x,y));
}
return 0;
}
七号板子题:
单点修改&单点查询(插入)
先介绍一下STL迭代器一个比较有用的工具(可我这个蒟蒻就只学会了用迭代器,,,0.0)
上代码:
//迭代器板子
#include<bits/stdvc++.h>
using namespace std;
int main()
{
int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
list<int> name(a,a+10);
list<int>::iterator it;
for (i = name.begin(); i != name.end(); i++)
cout << *i << ' ';
return 0;
}
也就是一道不错的练习线性分块的题,中心思想就是用好STL(主要是插入不会手打>.<)可以用vector也可以用list,(主要是不是很会要不就写了,,,)题解就是STL插入+重新建块优化(具体题解看注释)
#include<bits/stdc++.h>
#define sea 100005
#define LL long long
using namespace std;
int num,block,a[sea],s,n,x,y,z;
vector<int>belong[1050];
int st[200005],xn,r;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
void build()
{
block=sqrt(n);
for(int i=1;i<=n;i++)
belong[(i-1)/block+1].push_back(a[i]);
num=(n-1)/block+1;
}
pair<int,int> ins(int tt)
{
int a=1;
while(tt>belong[a].size())
tt-=belong[a].size(),a++;
return make_pair(a,tt-1);//记录其所在块序号x和在这块的tt-1处
}
void rebuild()
{
xn=0;
for(int i=1;i<=num;i++)
{
for(vector<int>::iterator j=belong[i].begin();j!=belong[i].end();j++)
//STL迭代器
st[++xn]=*j;
belong[i].clear();
}//清空块以及记录新块信息
int xblock=sqrt(xn);
for(int i=1;i<=xn;i++)
belong[(i-1)/xblock+1].push_back(st[i]);
num=(xn-1)/xblock+1;//重新分块
}
void alter_insert(int x,int y)
{
pair<int,int> t=ins(x);//插入
belong[t.first].insert(belong[t.first].begin()+t.second,y);
//遍历所有并处理
//中心思想就是用STL直接插入然后优化,复杂度在O(n)范围内
if(belong[t.first].size()>20*block)//特判重新分块
rebuild();
}
LL ask(int r)
{
pair<int,int> xx=ins(r);
return belong[xx.first][xx.second];
}
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read();
build();
for(int i=1;i<=n;i++)
{
s=read(); x=read(); y=read(); z=read();
if(s==0) alter_insert(x,y);
else printf("%lld\n",ask(y));
}
return 0;
}
八号板子题:
区间修改&单点查询(乘法)
(对于蒟蒻的我来说,越往后做越难了。。。)
区间加乘的修改,在线段树板子的时候也写过,但是分块就不是很好写,还是按照分块的中心思想即可,写的时候记得加上个懒标记就可以了,,,
#include<bits/stdc++.h>
#define sea 100005
#define stream 1005
#define LL long long
#define mod 10007
using namespace std;
int n,block,num,l[sea],belong[sea],r[sea],x,y,z,s;
int a[sea],ad[stream],mu[stream];//(很像是线段树里的懒标记)
inline LL read()
{
LL s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1; ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
void build()
{
block=sqrt(n); num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block; r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;
}
void ins(int t)
{
for(int i=l[t];i<=min(r[t],n);i++)
a[i]=(a[i]*mu[t]+ad[t])%mod;
ad[t]=0; mu[t]=1;
//相当于线段树中的pushdown
}
void alter(int s,int x,int y,int z)
{
ins(belong[x]);
for(int i=x;i<=min(r[belong[x]],y);i++)
{
if(s==0) a[i]+=z;
else a[i]*=z;
a[i]%=mod;
}//前半块
if(belong[x]!=belong[y])
{
ins(belong[y]);
for(int i=l[belong[y]];i<=y;i++)
{
if(s==0) a[i]+=z;
else a[i]*=z;
a[i]%=mod;
}
}//后半块
for(int i=belong[x]+1;i<belong[y];i++)
{
if(s==0) ad[i]=(ad[i]+z)%mod;
else
{
ad[i]=(ad[i]*z)%mod;
mu[i]=(mu[i]*z)%mod;
}
}//整块
}
LL ask(int tt)
{
return a[tt]*mu[belong[tt]]+ad[belong[tt]];
}
int main()
{
n=read(); block=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
build();
for(int i=1;i<=num;i++) mu[i]=1; //乘法预处理
for(int i=1;i<=n;i++)
{
s=read(); x=read(); y=read(); z=read();
if(s!=2) alter(s,x,y,z);
else printf("%lld\n",ask(y)%mod);
}
return 0;
}
九号板子题:
区间修改&区间查询(在线)
#include<bits/stdc++.h>
#define sea 100005
#define LL long long
using namespace std;
int n,block,num,l[sea],belong[sea],r[sea],x,y,z,a[sea],ins[sea];
inline LL read()
{
LL s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1; ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
void build()
{
block=sqrt(n); num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block; r[num]=n;
for(int i=1;i<=n;i++)
belong[i]=(i-1)/block+1;
}
void dye(int t)
{
if(ins[t]==-1) return ;
for(int i=l[t];i<=r[t];i++)
a[i]=ins[t];
ins[t]=-1;//染色
}
LL work(int x,int y,int z)
{
LL ans=0;
dye(belong[x]);
for(int i=x;i<=min(r[belong[x]],y);i++)
if(a[i]!=z) a[i]=z;
else ans++;
//前半块 (暴力染色)
if(belong[x]!=belong[y])
{
dye(belong[y]);
for(int i=l[belong[y]];i<=y;i++)
if(a[i]!=z) a[i]=z;
else ans++;
}
//后半块 (暴力染色)
for(int i=belong[x]+1;i<belong[y];i++)
if(ins[i]!=-1)
{
if(ins[i]!=z) ins[i]=z;
else ans+=block; //优化染色
}
else
{
for(int j=l[i];j<=r[i];j++)//重要的是要看好l[]r[]有傻掉的我写成了l[belong[i]],r[belong[i]];
if(a[j]!=z) a[j]=z;
else ans++;//没有染上色的整块就暴力染色
ins[i]=z;//染色剂
}
//整块
return ans;
}
int main()
{
memset(ins,-1,sizeof(ins));
n=read(); build();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++)
{
x=read(); y=read(); z=read();
printf("%lld\n",work(x,y,z));
}
return 0;
}
十号板子题:
区间众数
(最后一道好难啊,,,看了好长时间题解还没看懂)
还是要感谢大佬的题解:传送门
80分代码如下:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea = 1e5 + 5;
int n,m,a[sea],l,r,belong[sea],num[sea],block,t[sea],dp[1010][1010];
vector<int>v[sea];
inline LL read()
{
LL s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1; ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
void ins(int t)
{
int mode=0,maxx=0;
memset(num,0,sizeof(num));
for(int i=(t-1)*block+1;i<=n;i++)
{
num[a[i]]++;
if(num[a[i]]>maxx)
maxx=num[a[i]],mode=a[i];
if(maxx==num[a[i]]&&mode>a[i]) mode=a[i];
dp[t][belong[i]]=mode;
}
}
LL so(int l,int r,int tt)
{
vector<int>::iterator xx = upper_bound(v[tt].begin(), v[tt].end(), r);
vector<int>::iterator yy = lower_bound(v[tt].begin(), v[tt].end(), l);
return xx-yy;
}
LL ask(int x,int y)
{
int t1=belong[x],t2=belong[y];
int mode=dp[t1+1][t2-1];
int maxx=so(x,y,mode);
for(int i=x;i<=min(y,t1*block);i++)
{
int t=so(x,y,a[i]);
if(maxx<t||(maxx==t&&mode>a[i]))
maxx=t,mode=a[i];
}
if(t1!=t2)
{
for(int i=(t2-1)*block+1;i<=y;i++)
{
int t=so(x,y,a[i]);
if(maxx<t||(maxx==t&&mode>a[i]))
maxx=t,mode=a[i];
}
}
return mode;
}
int main()
{
n=read();
block=50;
for(int i=1;i<=n;i++)
{
a[i]=read(); t[i]=a[i];
belong[i]=(i-1)/block+1;
}
sort(t+1,t+1+n);
m=unique(t+1,t+1+n)-t-1;//离散化
for(int i=1;i<=n;i++)
a[i]=lower_bound(t+1,t+1+m,a[i])-t;
for(int i=1;i<=n;i++) v[a[i]].push_back(i);
for(int i=1;i<=belong[n];i++) ins(i);
for(int i=1;i<=n;i++)
{
l=read(); r=read();
printf("%d\n",t[ask(l,r)]);
}
return 0;
}
92分的代码如下:(大佬的,我的调了两个小时没跳出来,连静态查错都不行了~~o(>_<)o ~~)
#include <bits/stdc++.h>
using namespace std;
inline int read() {//读入挂
int ret = 0, c, f = 1;
for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
if(c == '-') f = -1, c = getchar();
for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
if(f < 0) ret = -ret;
return ret;
}
const int maxn = 1e5 + 5;
int m, block, n, a[maxn], l, r, belog[maxn], t[maxn];
int cnt[505][maxn], f[505][505]; //cnt[i][j]表示[0,i]块上数字j的数量
//f[i][j]表示第i块到第j块的众数是几
void init() {
for(int i = 1; i <= belog[n]; i++) {
for(int j = 1; j <= m; j++) {
cnt[i][j] += cnt[i - 1][j];
}
}
for(int i = 1; i <= belog[n]; i++) { //初始化f数组
int num[maxn];
int mode = 0;
int MAXnum = 0;
memset(num, 0, sizeof(num));
for(int j = (i - 1) * block + 1; j <= n; j++) {
num[a[j]]++;
if(num[a[j]] > MAXnum || (num[a[j]] == MAXnum && mode > a[j])) {
mode = a[j];
MAXnum = num[a[j]];
}
f[i][belog[j]] = mode;
}
}
}
void force(int l, int r) {
int num[maxn];
int mode = 0;
int MAXnum = 0;
memset(num, 0, sizeof(num));
for(int i = l; i <= r; i++) {
num[a[i]]++;
if(num[a[i]] > MAXnum || (num[a[i]] == MAXnum && mode > a[i])) {
mode = a[i];
MAXnum = num[a[i]];
}
}
printf("%d\n", t[mode]);
}
void solve(int t1, int t2, int l, int r, int*num, int &mode, int &MAXnum) {
for(int i = l; i <= r; i++) {
num[a[i]]++;
int tmp = num[a[i]] + cnt[t2 - 1][a[i]] - cnt[t1][a[i]];
if(tmp > MAXnum || (tmp == MAXnum && a[i] < mode)) {
mode = a[i];
MAXnum = tmp;
}
}
}
void query(int l, int r) {
int t1 = belog[l];
int t2 = belog[r];
if(t1 == t2 || t1 + 1 == t2) {//要分两种情况讨论
force(l, r);
return;
}
int mode = f[t1 + 1][t2 - 1];
int MAXnum = cnt[t2 - 1][mode] - cnt[t1][mode];
int num[maxn];
memset(num, 0, sizeof(num));
int L = l, R = t1 * block;
solve(t1, t2, L, R, num, mode, MAXnum);
L = (t2 - 1) * block + 1;
R = r;
solve(t1, t2, L, R, num, mode, MAXnum);
printf("%d\n", t[mode]);
}
int main() {
n = read();
block = 2 * sqrt(n);
for(int i = 1; i <= n; i++) {
a[i] = read();
t[i] = a[i];
belog[i] = (i - 1) / block + 1;
}
sort(t + 1, t + 1 + n);
m = unique(t + 1, t + 1 + n) - t - 1;
for(int i = 1; i <= n; i++) {
a[i] = lower_bound(t + 1, t + 1 + m, a[i]) - t;
cnt[belog[i]][a[i]]++;
}
init();
for(int i = 1; i <= n; i++) {
l = read();
r = read();
query(l, r);
}
return 0;
}
还有一道比较好的分块题:
蒲公英(区间在线众数)
来补题了,,,,
好早以前的一道分块题,由于今天要讲莫队就顺便想到了还有一道这样的题就过来写了一下,(没想到是个黑题,!!!∑(゚Д゚ノ)ノ)
题解:
(尽管这如果不是强制在线的话就是一个裸的莫队题,但是这是个强制在线,结果莫队就败了,就只能用分块了,,)
分块众数+离散化
原来是用vector写的,然后由于vector太慢而且不会离散化就死掉了,就改成数组+离散化写,思想呢,是参考洛谷上的大佬: hkr04 的思想。具体解释看代码。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=40000+10;
int a[maxn],b[maxn],t[maxn],l[maxn],r[maxn],s[200+10][maxn],belong[maxn];//s[i][j]表示在前i块中b[j]出现的次数
int f[210][210];//f[i][j]表示从第i个块到第j个块中的众数
int block;//每块长度
int read()
{
int x=0,p=1;char ch=getchar();
while(ch<'0'||ch>'9'){if (ch=='-')p=-1;ch=getchar();}
while('0'<=ch&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*p;
}
int main()
{
int n=read(),m=read();
block=sqrt(n);//每一块的长度
for(int i=1;i<=n;i++) b[i]=a[i]=read();
sort(b+1,b+1+n);
int sum=unique(b+1, b+1+n)-b-1,cnt=(n-1)/block+1;
//sum是不同的数值个数,cnt是总块数
for (int i=1;i<=n;i++)
a[i]=lower_bound(b+1, b+1+sum, a[i])-b;//这一部分是离散化,a存储的是a[i]在b中的位置
for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
for(int i=1;i<=belong[n];i++) l[i]=(i-1)*block+1,r[i]=i*block;
for(int i=1;i<=n;i++) s[belong[i]][a[i]]++;//预处理出f和s
for(int i=1;i<=cnt;i++)
for(int j=1;j<=sum;j++)
s[i][j]+=s[i-1][j];//前缀和
for(int i=1;i<=cnt;i++)
for(int j=i;j<=cnt;j++)
{
int mx=f[i][j-1];
for (int k=l[j];k<=min(r[j],n);k++)//不需要从头开始扫
if ((s[j][a[k]]-s[i-1][a[k]]>s[j][mx]-s[i-1][mx])||(s[j][a[k]]-s[i-1][a[k]]==s[j][mx]-s[i-1][mx]&&a[k]<mx))
mx=a[k];
f[i][j]=mx;
}
int x=0;
while(m--)
{
int L=(read()+x-1)%n+1,R=(read()+x-1)%n+1;
if (L>R) swap(L, R);
int bl=belong[L],br=belong[R],mx=0;//bl是l所属块编号,br是r所属块的编号,mx是众数在b中的下标
if (br-bl<=1)//如果区间长度小于等于两格,直接暴力
{
for (int i=L;i<=R;i++) t[a[i]]++;
for (int i=L;i<=R;i++)
if (t[a[i]]>t[mx]||(t[a[i]]==t[mx]&&a[i]<mx)) mx=a[i];
for (int i=L;i<=R;i++) t[a[i]]=0;//将桶清空
}
else
{
for (int i=L;i<=min(r[bl],n);i++) t[a[i]]++;
for (int i=l[br];i<=R;i++) t[a[i]]++;
mx=f[bl+1][br-1];
for (int i=L;i<=min(r[bl],n);i++)//考虑蓝色部分出现的数是否会成为众数
{
int pre=t[mx]+s[br-1][mx]-s[bl][mx],now=t[a[i]]+s[br-1][a[i]]-s[bl][a[i]];
if (now>pre||(now==pre&&mx>a[i]))
mx=a[i];
}
for (int i=l[br];i<=R;i++)
{
int pre=t[mx]+s[br-1][mx]-s[bl][mx],now=t[a[i]]+s[br-1][a[i]]-s[bl][a[i]];
if (now>pre||(now==pre&&mx>a[i]))
mx=a[i];
}
//将桶清空
for (int i=L;i<=min(r[bl], n);i++) t[a[i]]=0;
for (int i=l[br];i<=R;i++) t[a[i]]=0;
}
x=b[mx];
printf("%d\n",x);
}
return 0;
}
(提醒:看了看hzwer学长的代码就觉得,,,过不去大数据啊)
做个小总结:
整个分块学习,还是比较顺的,(毕竟还没有学到比较难懂的地方,但倒数第二道题几乎只有思路代码就挂了,,)总结起来呢就是还是理解分块的中心思想比较重要,而且还比较好理解,就是把区间分成三部分,一部分用等长度的块解决,剩下的两部分边界的直接用暴力处理即可,所以是sqrt(n)的复杂度还是比较好理解的,以后做数据结构题的时候就多往分块的方向想想,(毕竟GZ大佬曾经说过:分块能解决一切问题)。