由于本篇主要讲分块,其他算法可能更优但我不加赘述
注释:N表示n,m表示块数即sqrt(n)
一.数列分块入门1
loj6277
题意简述:
区间加法,单点询问
fenkuai(简称fk)[i]表示整块中需要加的数;
处理时,整块加法:fk[i]+=c;
散块加法:将整块fk[i]值赋回num[j],然后将需要的num[j]+=c;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=50005,M=505;
int n,ns,g;
int num[N],bl[N];
//l:(i-1)*ns+1 r:i*ns
int fk[M];
void putin(int l,int r,int v)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(int i=l;i<=r;i++)
num[i]+=v;
return ;
}
if((x-1)*ns+1<l)
{
for(int i=l;i<=x*ns;i++)
num[i]+=v;
x++;
}//左散块
if(r<y*ns)
{
for(int i=(y-1)*ns+1;i<=r;i++)
num[i]+=v;
y--;
}//右散块
for(int i=x;i<=y;i++)
{
fk[i]+=v;
}//整块
}
signed main(){
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);
ns=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if((i-1)%ns==0)g++;
bl[i]=g;
}
for(int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op==0)
{
putin(l,r,c);
}
else
{
printf("%d\n",num[r]+fk[bl[r]]);
}
}
}
二.数列分块入门2
loj6278
题意简述:
区间加法,询问区间小于v个数
b[N]数组表示单块排序后的结果
fk[M]表示整块加法缓存
处理时,散块暴力枚举一遍,满足就ans++;
整块就根据b[]中的区间,二分
注:散块加法时需要重新排序,但整块不需要(很显然)。
#include<bits/stdc++.h>
#define ll long long
#define re register
const int N=50005,M=1005;
int n,ns,g;
int num[N],bl[N];
int a[N];
//l:(i-1)*ns+1 r:i*ns
int fk[M],L[M],R[M];
inline int tp(int x){return num[x]+fk[bl[x]];}
inline void up(int x,int y)
{
for(re int i=L[x];i<=R[y];i++)a[i]=num[i];
for(re int i=x;i<=y;i++)
{
std::sort(a+L[i],a+R[i]+1);
}
}
void putin(int l,int r,int v)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(re int i=l;i<=r;i++)
num[i]+=v;
up(x,y);
return ;
}
if(L[x]<l)
{
for(re int i=l;i<=R[x];i++)
num[i]+=v;
up(x,x);
x++;
}
if(r<R[y])
{
for(re int i=L[y];i<=r;i++)
num[i]+=v;
up(y,y);
y--;
}
for(re int i=x;i<=y;i++)
{
fk[i]+=v;
}
}
int solve(int l,int r,int v)
{
int x=bl[l],y=bl[r],ans=0;
if(y-x<=2)
{
for(re int i=l;i<=r;i++)
if(tp(i)<v)ans++;
return ans;
}
if(L[x]<l)
{
for(re int i=l;i<=R[x];i++)
if(tp(i)<v)ans++;
x++;
}
if(r<R[y])
{
for(re int i=L[y];i<=r;i++)
if(tp(i)<v)ans++;
y--;
}
for(re int i=x;i<=y;i++)
{
int lt=L[i],rt=R[i],mid;
if(a[lt]+fk[bl[lt]]>=v)continue;
while(lt<rt)
{
mid=(lt+rt)>>1;
if(a[mid]+fk[bl[mid]]>=v)rt=mid;
else lt=mid+1;
}
if(a[lt]+fk[bl[lt]]<v)ans+=lt-L[i]+1;
else ans+=lt-L[i];
}return ans;
}
signed main(){
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);
ns=sqrt(n);
for(re int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
up(1,g);
for(re int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op==0)
{
putin(l,r,c);
}
else
{
printf("%d\n",solve(l,r,c*c));
}
}
}
三.数列分块入门3
loj6279
题意简述:
区间加法,询问x前驱
与数列分块2类似,区别:数据大小,从求个数改为求num[]值。
#include<bits/stdc++.h>
#define ll long long
#define re register
const int N=100005,M=1005,INF=1e10;
int n,ns,g;
int num[N],bl[N];
int a[N];
//l:(i-1)*ns+1 r:i*ns
int fk[M],L[M],R[M];
inline int tp(int x){return num[x]+fk[bl[x]];}
inline void up(int x,int y)
{
for(re int i=L[x];i<=R[y];i++)a[i]=num[i];
for(re int i=x;i<=y;i++)
{
std::sort(a+L[i],a+R[i]+1);
}
}
void putin(int l,int r,int v)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(re int i=l;i<=r;i++)
num[i]+=v;
up(x,y);
return ;
}
if(L[x]<l)
{
for(re int i=l;i<=R[x];i++)
num[i]+=v;
up(x,x);
x++;
}
if(r<R[y])
{
for(re int i=L[y];i<=r;i++)
num[i]+=v;
up(y,y);
y--;
}
for(re int i=x;i<=y;i++)
{
fk[i]+=v;
}
}
ll solve(int l,int r,int v)
{
int x=bl[l],y=bl[r];ll ans=-INF;
if(y-x<=2)
{
for(re int i=l;i<=r;i++)
if(tp(i)<v&&tp(i)>ans)ans=tp(i);
return ans;
}
if(L[x]<l)
{
for(re int i=l;i<=R[x];i++)
if(tp(i)<v&&tp(i)>ans)ans=tp(i);
x++;
}
if(r<R[y])
{
for(re int i=L[y];i<=r;i++)
if(tp(i)<v&&tp(i)>ans)ans=tp(i);
y--;
}
for(re int i=x;i<=y;i++)
{
int lt=L[i],rt=R[i],mid,tmp;
if(a[lt]+fk[bl[lt]]>=v)continue;
while(lt<rt)
{
mid=(lt+rt)>>1;
if(a[mid]+fk[bl[mid]]>=v)rt=mid;
else lt=mid+1;
}
if(a[lt]+fk[bl[lt]]<v)tmp=a[lt]+fk[bl[lt]];
else tmp=a[lt-1]+fk[bl[lt-1]];
if(tmp<v&&tmp>ans)ans=tmp;
}return ans;
}
signed main(){
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);
ns=sqrt(n);
for(re int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
up(1,g);
for(re int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op==0)
{
putin(l,r,c);
}
else
{
ll tmp=solve(l,r,c);
if(tmp==-INF)printf("-1\n");
else printf("%d\n",tmp);
}
}
}
四.数列分块入门4
loj6280
题意简述:
区间加法, 区间求和
fk[M]表示区间总和
tag[M]表示整块加法缓存
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=500005,M=1005;
int n,ns,g;
ll num[N],bl[N];
int L[M],R[M];
ll fk[M],tag[M];
void putin(int l,int r,int v)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(int i=l;i<=r;i++)
num[i]+=v,fk[bl[i]]+=v;
return ;
}
if(L[x]<l)
{
for(int i=l;i<=R[x];i++)
num[i]+=v,fk[bl[i]]+=v;
x++;
}
if(r<R[y])
{
for(int i=L[y];i<=r;i++)
num[i]+=v,fk[bl[i]]+=v;
y--;
}
for(int i=x;i<=y;i++)
{
tag[i]+=v;
fk[i]=fk[i]+v*(R[i]-L[i]+1);
}
}
int solve(int l,int r,ll v)
{
int x=bl[l],y=bl[r];ll ans=0;v++;
if(y-x<=2)
{
for(int i=l;i<=r;i++)
ans=(ans+(num[i]+tag[bl[i]])%v)%v;
return ans;
}
if(L[x]<l)
{
for(int i=l;i<=R[x];i++)
ans=(ans+(num[i]+tag[bl[i]])%v)%v;
x++;
}
if(r<R[y])
{
for(int i=L[y];i<=r;i++)
ans=(ans+(num[i]+tag[bl[i]])%v)%v;
y--;
}
for(int i=x;i<=y;i++)
{
ans=(ans+fk[i])%v;
}
return ans;
}
int main()
{
//
scanf("%d",&n);
ns=sqrt(n);if(!ns)ns++;
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
for(int i=1;i<=g;i++)
{
for(int j=L[i];j<=R[i];j++)
fk[i]+=num[j];
}
for(int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op==0)
{
putin(l,r,c);
}
else
{
printf("%d\n",solve(l,r,c));
}
}
}
五.数列分块入门5
loj6281
题意简述:
区间开方(向下取整), 区间求和
找规律可以发现一个小规律:2^31中任意数开5次后为1,0,然后继续开方都始终为1,0.
fk[M]开方次数
pr[M]计算了多少次开方
st[M]计算了pr[M]次开方后的区间总和
处理询问时,散块就将对应块拆开,需要的num[i]开方;
整块时,若pr[j]>=5或pr[j]==fk[j]则调用st[j];否则计算fk[j]-pr[j]次,整块重新算遍更新st[j]值,pr[j]=fk[j];
#include<bits/stdc++.h>
using namespace std;
const int N=50005,M=505;
int n,ns,g;
int num[N],bl[N];
int L[M],R[M],fk[M];
int st[M],pr[M];
void putin(int l,int r)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(int i=l;i<=r;i++)
num[i]=sqrt(num[i]);
return ;
}
if(L[x]<l){for(int i=l;i<=R[x];i++)num[i]=sqrt(num[i]);x++;}
if(r<R[y]){for(int i=L[y];i<=r;i++)num[i]=sqrt(num[i]);y--;}
for(int i=x;i<=y;i++)
{
if(fk[i]>=5)continue;
fk[i]++;
if(fk[i]==5)
{
int gs=5-pr[i];pr[i]=5;
for(int k=L[i];k<=R[i];k++)
for(int c=1;c<=gs;c++)num[k]=sqrt(num[k]);
st[i]=0;
for(int k=L[i];k<=R[i];k++)st[i]+=num[k];
}
}
}
int sum(int x){int ans=num[x],c=fk[bl[x]]-pr[bl[x]];for(int i=1;i<=c;i++)ans=sqrt(ans);return ans;}
int solve(int l,int r)
{
int x=bl[l],y=bl[r],ans=0;
if(y-x<=2)
{
for(int i=l;i<=r;i++)ans+=sum(i);
return ans;
}
if(L[x]<l){for(int i=l;i<=R[x];i++)ans=ans+sum(i);x++;}
if(r<R[y]){for(int i=L[y];i<=r;i++)ans=ans+sum(i);y--;}
for(int i=x;i<=y;i++)
{
if(fk[i]>=5){ans+=st[i];continue;}
for(int j=L[i];j<=R[i];j++)num[j]=sum(j);
pr[i]=fk[i];
for(int j=L[i];j<=R[i];j++)ans=ans+num[j];
}
return ans;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);ns=sqrt(n);if(!ns)ns++;
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
for(int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op==0)
{
putin(l,r);
}
else
{
printf("%d\n",solve(l,r));
}
}
return 0;
}
六.数列分块入门6
loj6282
题意简述:
单点插入,单点询问
用vector<>装块内num[i],插入就用insert()插入,插入到足够长就重新分块:将vetor值赋回num[],再重新分块num[]
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=1005;
int n,ns,g;
int m;
int a[N];
vector<int>v[M];
void toa()
{
m=0;
for(int i=1;i<=g;i++)
for(int j=0;j<v[i].size();j++)
a[++m]=v[i][j];
}
void init()
{
for(int i=1;i<=g;i++)v[i].clear();
g=0;
for(int i=1;i<=m;i++)
{
if((i-1)%ns==0)g++;
v[g].push_back(a[i]);
}
}
void putin(int k,int c)
{
int p=1;
while(k>v[p].size())k-=v[p].size(),p++;
v[p].insert(v[p].begin()+k-1,c);
m++;
if(v[p].size()>ns*10)toa(),init();
}
int solve(int k)
{
int p=1;
while(k>v[p].size())k-=v[p].size(),p++;
return v[p][k-1];
}
void pt(int x=-1){if(x!=-1)printf("%d ",x);else printf("\n");}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);ns=sqrt(n);if(!ns)ns++;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
m=n;init();
for(int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op==0)
{
putin(l,r);
}
else
{
printf("%d\n",solve(r));
}
}
}
七.数列分块入门7
loj6283
题意简述:
区间加法,区间乘法,单点询问
考虑用fk1[],fk2[]表示加法缓存和乘法缓存。
具体实现类似于数列分块入门1
注:区间乘法应该将乘法缓存和加法缓存一起*c
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=405,mod=10007;
int n,ns,g;
int a[N],bl[N];
int L[M],R[M],fk1[M],fk2[M];
int sum(int x){return (a[x]*fk2[bl[x]]%mod+fk1[bl[x]]%mod)%mod;}
void updata(int x){for(int i=L[x];i<=R[x];i++)a[i]=sum(i);fk1[x]=0;fk2[x]=1;}
void add(int l,int r,int c)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(int i=x;i<=y;i++)updata(i);
for(int i=l;i<=r;i++)
a[i]=(a[i]+c)%mod;
return ;
}
if(L[x]<l){updata(x);for(int i=l;i<=R[x];i++)a[i]=(a[i]+c)%mod;x++;}
if(r<R[y]){updata(y);for(int i=L[y];i<=r;i++)a[i]=(a[i]+c)%mod;y--;}
for(int i=x;i<=y;i++)
{
fk1[i]=(fk1[i]+c)%mod;
}
}
void mul(int l,int r,int c)
{
int x=bl[l],y=bl[r];
if(y-x<=2)
{
for(int i=x;i<=y;i++)updata(i);
for(int i=l;i<=r;i++)
a[i]=(a[i]*c)%mod;
return ;
}
if(L[x]<l){updata(x);for(int i=l;i<=R[x];i++)a[i]=(a[i]*c)%mod;x++;}
if(r<R[y]){updata(y);for(int i=L[y];i<=r;i++)a[i]=(a[i]*c)%mod;y--;}
for(int i=x;i<=y;i++)
{
fk1[i]=(fk1[i]*c)%mod;
fk2[i]=(fk2[i]*c)%mod;
}
}
void pr()
{
for(int i=1;i<=n;i++)printf("%d ",sum(i));printf("\n");
}
signed main(){
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);ns=sqrt(n);if(!ns)ns++;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);a[i]%=mod;
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
for(int i=1;i<=g;i++)fk2[i]=1;
for(int op,l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d%d",&op,&l,&r,&c);
c%=mod;
if(op==0)
{
add(l,r,c);
}
else if(op==1)
{
mul(l,r,c);
}
else
{
printf("%d\n",sum(r));
}
//pr();
}
}
八.数列分块入门8
loj6284
题意简述:
询问区间有多少个c
块内排序,二分查找。。。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=405;
int n,ns,g;
int a[N],b[N],bl[N];
int L[M],R[M];
int vis[M];
int solve(int l,int r,int c)
{
int x=bl[l],y=bl[r],ans=0;
if(y-x<=2)
{
for(int i=x;i<=y;i++)
{
if(vis[i]){for(int j=L[i];j<=R[i];j++)a[j]=vis[i];}
vis[i]=0;
}
for(int i=l;i<=r;i++)
{
if(a[i]==c)ans++;
a[i]=c;
}
return ans;
}
if(L[x]<l)
{
if(vis[x]){for(int j=L[x];j<=R[x];j++)a[j]=vis[x];}
for(int i=l;i<=R[x];i++)
{
if(a[i]==c)ans++;
a[i]=c;
}
vis[x]=0;x++;
}
if(r<R[y])
{
if(vis[y]){for(int j=L[y];j<=R[y];j++)a[j]=vis[y];}
for(int i=L[y];i<=r;i++)
{
if(a[i]==c)ans++;
a[i]=c;
}
vis[y]=0;y--;
}
for(int i=x;i<=y;i++)
{
if(vis[i]){if(vis[i]==c)ans=ans+R[i]-L[i]+1;vis[i]=c;continue;}
for(int j=L[i];j<=R[i];j++)
{
if(a[j]==c)ans++;
}
vis[i]=c;
}return ans;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);ns=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
for(int l,r,c,i=1;i<=n;i++)
{
scanf("%d%d%d",&l,&r,&c);
printf("%d\n",solve(l,r,c));
}
}
九.数列分块入门9
loj6285
题意简述:
区间众数
离散(方便桶排)
预处理:cnt[i][j]表示i号块有多少个j
fk[i][ii]表示i->ii块的众数,gs[i][ii]表示i->ii块的众数出现次数
处理时:散块中出现次数+整块中出现次数>g,就更新ans,g;
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=405;
int n,ns,g,tot;
int L[M],R[M];
int a[N],ys[N],bl[N];
struct nd{int num,id;}b[N];
int cnt[M][N],fk[M][M],gs[M][M];
bool cmp(const nd x,const nd y){return x.num<y.num;}
void bein()
{
for(int i=1;i<=g;i++)
{
for(int j=L[i];j<=R[i];j++)
cnt[i][a[j]]++;
for(int j=1;j<=tot;j++)
{
cnt[i][j]+=cnt[i-1][j];
}
}
for(int i=1;i<=g;i++)
for(int j=i;j<=g;j++)
{
fk[i][j]=fk[i][j-1];
int mx=fk[i][j];
gs[i][j]=cnt[j][mx]-cnt[i-1][mx];
for(int k=L[j];k<=R[j];k++)
{
int c=a[k];
int tmp=cnt[j][c]-cnt[i-1][c];
if(tmp>gs[i][j]||(tmp==gs[i][j]&&fk[i][j]>c))
{
gs[i][j]=tmp;
fk[i][j]=c;
}
}
}
}
int t[N];
int solve(int l,int r)
{
int x=bl[l],y=bl[r],ans=0,gg=0;
if(y-x<=2)
{
for(int i=l;i<=r;i++)
{
int c=a[i];t[c]++;
if(t[c]>gg||(t[c]==gg&&ans>c))
{
gg=t[c];
ans=c;
}
}
for(int i=l;i<=r;i++)t[a[i]]=0;
return ans;
}
int lt=l-1,rt=r+1;
if(L[x]<l){lt=R[x];x++;}
if(r<R[y]){rt=L[y];y--;}
for(int i=l;i<=lt;i++)
{
int c=a[i];t[c]++;
int tmp=cnt[y][c]-cnt[x-1][c]+t[c];
if(tmp>gg||(tmp==gg&&ans>c))
{
gg=tmp;
ans=c;
}
}
for(int i=rt;i<=r;i++)
{
int c=a[i];t[c]++;
int tmp=cnt[y][c]-cnt[x-1][c]+t[c];
if(tmp>gg||(tmp==gg&&ans>c))
{
gg=tmp;
ans=c;
}
}
if(gs[x][y]>gg||(gs[x][y]==gg&&ans>fk[x][y]))
{
gg=gs[x][y];
ans=fk[x][y];
}
for(int i=l;i<=lt;i++)t[a[i]]=0;
for(int i=rt;i<=r;i++)t[a[i]]=0;
return ans;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.ans","w",stdout);
scanf("%d",&n);ns=sqrt(n);if(!ns)ns++;
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i].num);b[i].id=i;
if((i-1)%ns==0){R[g]=i-1;g++;L[g]=i;}
bl[i]=g;
}R[g]=n;
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(b[i].num!=b[i-1].num)
{ys[++tot]=b[i].num;}
a[b[i].id]=tot;
}
bein();
for(int l,r,i=1;i<=n;i++)
{
scanf("%d%d",&l,&r);
printf("%d\n",ys[solve(l,r)]);
}
}