hzwer博客九题详解:http://hzwer.com/8053.html
分块专题题目地址:https://loj.ac/problems/search?keyword=%E5%88%86%E5%9D%97
总结:最近在刷算法竞赛的时候看到了这个专题,就刷了前面几道题练习练习分块的写法。
首先是读入数组和数组分块的操作
n=read();
m = sqrt(n);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)block[i]=(i-1)/m+1;
然后是区间加分,其中ad数组用来标记整个区间的增值
void add(int l,int r,int x)
{
for(int i=l;i<=min(block[l]*m,r);i++)a[i]+=x;
if(block[l]!=block[r])
{
for(int i=(block[r]-1)*m+1;i<=r;i++)a[i]+=x;
}
for(int i=block[l]+1;i<=block[r]-1;i++)ad[i]+=x;
}
分块1:区间加法,单点查值,分块的模板。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn = 50005;
#define ll long long
int n,m,a[maxn],block[maxn],ad[maxn];
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void add(int l,int r,int x)
{
for(int i=l;i<=min(block[l]*m,r);i++)a[i]+=x;
if(block[l]!=block[r])
{
for(int i=(block[r]-1)*m+1;i<=r;i++)a[i]+=x;
}
for(int i=block[l]+1;i<=block[r]-1;i++)ad[i]+=x;
}
int main() {
n=read();
m = sqrt(n);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)block[i]=(i-1)/m+1;
for(int i=1;i<=n;i++)
{
int x,y,z,k;
x=read();
y=read();
z=read();
k=read();
if(x==0)add(y,z,k);
else printf("%d\n",a[z]+ad[block[z]]);
}
return 0;
}
分块2:区间加法,询问区间内小于一个值的个数,建区间和添加的时候将区间排序,查询的时候区间前面和最后都暴力即可,在整个区间内进行二分查找该值的位置。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 50005;
#define _for(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
ll read()
{
ll x=0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch = getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,a[maxn],block[maxn],add[maxn];
vector<int> Q[maxn];
void getsort(int x)
{
Q[x].clear();
_for(i,(x-1)*m+1,x*m)Q[x].push_back(a[i]);
sort(Q[x].begin(),Q[x].end());
}
void addnum(int l,int r,int x)
{
_for(i,l,min(block[l]*m,r))a[i]+=x;
getsort(block[l]);
if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)a[i]+=x;
getsort(block[r]);
_for(i,block[l]+1,block[r]-1)add[i]+=x;
}
int query(int l,int r,int x)
{
int sum = 0;
_for(i,l,min(block[l]*m,r))if(a[i]+add[block[i]]<x)sum++;
if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)if(a[i]+add[block[i]]<x)sum++;
_for(i,block[l]+1,block[r]-1)sum+=lower_bound(Q[i].begin(),Q[i].end(),x-add[i])-Q[i].begin();
return sum;
}
int main(int argc, char const *argv[])
{
n = read();
m = sqrt(n);
_for(i,1,n)a[i]=read();
_for(i,1,n)
{
block[i]=(i-1)/m+1;
Q[block[i]].push_back(a[i]);
}
_for(i,1,(n-1)/m+1)
{
sort(Q[i].begin(),Q[i].end());
}
_for(i,1,n)
{
int x,l,r,k;
x=read();
l=read();
r=read();
k=read();
if(x==0)addnum(l,r,k);
else
{
int num = k*k;
printf("%d\n",query(l,r,num));
}
}
return 0;
}
分块3:区间加法,询问区间内小于x的最大值。与第二题类似,还是排序查找即可,不完整区间暴力,区间二分,最后找其中的最大值;也可以在分块内使用set自动排序。该题sqrt分块会T,得分100。。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
const int maxn = 100005;
#define _for(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
ll read()
{
ll x=0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch = getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,a[maxn],block[maxn],add[maxn];
set<int> Q[105];
void addnum(int l,int r,int x)
{
_for(i,l,min(r,block[l]*m))
{
Q[block[l]].erase(a[i]);
a[i]+=x;
Q[block[l]].insert(a[i]);
}
if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)
{
Q[block[r]].erase(a[i]);
a[i]+=x;
Q[block[r]].insert(a[i]);
}
_for(i,block[l]+1,block[r]-1)add[i]+=x;
}
int query(int l,int r,int x)
{
int ans = -1;
_for(i,l,min(block[l]*m,r))if(a[i]+add[block[l]]<x)ans=max(ans,a[i]+add[block[l]]);
if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)if(a[i]+add[block[r]]<x)ans=max(ans,a[i]+add[block[r]]);
_for(i,block[l]+1,block[r]-1)
{
set<int>::iterator it = Q[i].lower_bound(x-add[i]);
if(it==Q[i].begin())continue;
--it;
ans = max(ans,*it+add[i]);
}
return ans;
}
int main()
{
n=read();
m=1500;
_for(i,1,n)a[i]=read();
_for(i,1,n)
{
block[i]=(i-1)/m+1;
Q[block[i]].insert(a[i]);
}
_for(i,1,n)
{
int x,y,z,k;
x=read(),y=read(),z=read(),k=read();
if(x==0)addnum(y,z,k);
else printf("%d\n",query(y,z,k));
}
}
分块4:区间加法,查询区间和。记录每个区间和,在更新的时候重新记录区间和与标记区间。查询的时候与第一题类似。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
const int maxn = 50005;
#define _for(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
ll read()
{
ll x=0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch = getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,block[maxn];
ll a[maxn],add[maxn],sum[maxn];
void addnum(int l,int r,int x)
{
_for(i,l,min(r,block[l]*m))
{
a[i]+=x;
sum[block[l]]+=x;
}
if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)
{
a[i]+=x;
sum[block[r]]+=x;
}
_for(i,block[l]+1,block[r]-1)add[i]+=x;
}
ll query(int l,int r)
{
ll ans = 0;
_for(i,l,min(r,block[l]*m))ans=(ans+a[i]+add[block[l]]);
if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)ans=(ans+a[i]+add[block[r]]);
_for(i,block[l]+1,block[r]-1)
{
ans=ans+add[i]*m+sum[i];
}
return ans;
}
int main(int argc, char const *argv[])
{
n=read();
m=sqrt(n);
_for(i,1,n)a[i]=read();
_for(i,1,n)
{
block[i]=(i-1)/m+1;
sum[block[i]]+=a[i];
}
_for(i,1,n)
{
int x,y,z,k;
x=read(),y=read(),z=read(),k=read();
if(x==0)addnum(y,z,k);
else printf("%lld\n",query(y,z)%(k+1));
}
return 0;
}