一转眼,已经有整整一年没有在博客园写博客了。去洛谷写了几篇(How time flys.

最近突然想起其实我不太擅长分块算法,又想起去年暑假有位同学同我提起过LOJ的数列分块九题,说来惭愧,打了这么久题今天才打算做这个系列,甚至连LOJ的账号都没有注册。。。于是今天花费了半天时间敲题,但是更尴尬的是只打了前四题,分块细节真的是相当繁琐。

分块是一种优雅的暴力,时间复杂度为根号n量级,虽然同线段树树状数组之类的logn量级的复杂度无法相提并论,但是在大多数情况下,分块都可以比较好地替代线段树。其代码量较少且逻辑相较线段树而言更清晰尤其是可以进行块内二分之类的操作,从而有的时候比线段树更有优势。

分块,顾名思义,就是将数列分为多块进行考虑,在进行区间修改或者区间查询操作时,使用分块算法,我们可以直接处理区间中的块,再处理零散块。我们可以将数列划分为根号n个块,从而将时间复杂度降低至根号n。

#6277. 数列分块入门 1 - 题目 - LibreOJ (loj.ac)

区间加法,单点查值,分块水题(变量名想半天)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=(5e4)+5;
ll a[maxn];
ll l[maxn];
ll r[maxn];
ll bel[maxn];
ll lazy_add[maxn];
void build(ll n){
ll block=sqrt(n);
ll num=n/block;
if(n%block)num++;
for(int i=1;i<=num;i++){
l[i]=(i-1)*block+1;
r[i]=min(n,i*block);
lazy_add[i]=0;
}
for(int i=1;i<=n;i++){
bel[i]=(i-1)/block+1;
}
}
void add(ll L,ll rr,ll c){
ll s=bel[L]+1;
ll t=bel[rr]-1;
for(int i=L;i<=min(rr,r[s-1]);i++){
a[i]+=c;
}
if(bel[L]==bel[rr])return;
for(int i=l[t+1];i<=rr;i++){
a[i]+=c;
}
for(int i=s;i<=t;i++){
lazy_add[i]+=c;
}
}
void calc(ll x){
ll p=bel[x];
for(int i=l[p];i<=r[p];i++){
a[i]+=lazy_add[p];
}
lazy_add[p]=0;
cout<<a[x]<<endl;
}
int main(){
ll n;
cin>>n;
build(n);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
ll opt,L,rr,c;
cin>>opt>>L>>rr>>c;
if(!opt){
add(L,rr,c);
}
else{
calc(rr);
}
}
return 0;
}

#6278. 数列分块入门 2 - 题目 - LibreOJ (loj.ac)

块内二分即可(线段树不行),注意边角的处理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=(5e4)+5;
int L[maxn],R[maxn];
int bel[maxn];
ll a[maxn];
ll b[maxn];
ll tmp[maxn];
ll tag[maxn];
void build(int n){
int block=sqrt(n);
int num=n/block;
if(n%block)num++;
for(int i=1;i<=num;i++){
L[i]=(i-1)*block+1;
R[i]=min(n,i*block);
sort(a+L[i],a+R[i]+1);
for(int j=L[i];j<=R[i];j++){
bel[j]=i;
}
}
}
void update(int l,int r,ll c){
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++)b[i]+=c;
for(int i=L[bel[l]];i<=R[bel[l]];i++){
tmp[i]=b[i];
}
sort(tmp+L[bel[l]],tmp+R[bel[l]]+1);
for(int i=L[bel[l]];i<=R[bel[l]];i++){
a[i]=tmp[i];
}
}
else{
for(int i=l;i<=R[bel[l]];i++){
b[i]+=c;
}
for(int i=L[bel[l]];i<=R[bel[l]];i++){
tmp[i]=b[i];
}
sort(tmp+L[bel[l]],tmp+R[bel[l]]+1);
for(int i=L[bel[l]];i<=R[bel[l]];i++){
a[i]=tmp[i];
}
for(int i=L[bel[r]];i<=r;i++){
b[i]+=c;
}
for(int i=L[bel[r]];i<=R[bel[r]];i++){
tmp[i]=b[i];
}
sort(tmp+L[bel[r]],tmp+R[bel[r]]+1);
for(int i=L[bel[r]];i<=R[bel[r]];i++){
a[i]=tmp[i];
}
for(int i=bel[l]+1;i<bel[r];i++){
tag[i]+=c;
}
}
}
void calc(int l,int r,ll c){
ll ans=0;
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
if(b[i]+tag[bel[l]]<c)ans++;
}
}
else{
for(int i=l;i<=R[bel[l]];i++){
if(b[i]+tag[bel[l]]<c)ans++;
}
for(int i=L[bel[r]];i<=r;i++){
if(b[i]+tag[bel[r]]<c)ans++;
}
for(int i=bel[l]+1;i<bel[r];i++){
ans+=lower_bound(a+L[i],a+R[i]+1,c-tag[i])-a-L[i];
}
}
cout<<ans<<endl;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i];
build(n);
for(int i=1;i<=n;i++){
ll opt,l,r,c;
cin>>opt>>l>>r>>c;
if(!opt){
update(l,r,c);
}
else{
calc(l,r,c*c);
}
}
return 0;
}

#6279. 数列分块入门 3 - 题目 - LibreOJ (loj.ac)

前一题修改一下即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=(1e5)+5;
int L[maxn],R[maxn];
int bel[maxn];
ll a[maxn];
ll b[maxn];
ll tmp[maxn];
ll tag[maxn];
void build(int n){
int block=sqrt(n);
int num=n/block;
if(n%block)num++;
for(int i=1;i<=num;i++){
L[i]=(i-1)*block+1;
R[i]=min(n,i*block);
sort(a+L[i],a+R[i]+1);
for(int j=L[i];j<=R[i];j++){
bel[j]=i;
}
}
}
void update(int l,int r,ll c){
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++)b[i]+=c;
for(int i=L[bel[l]];i<=R[bel[l]];i++){
tmp[i]=b[i];
}
sort(tmp+L[bel[l]],tmp+R[bel[l]]+1);
for(int i=L[bel[l]];i<=R[bel[l]];i++){
a[i]=tmp[i];
}
}
else{
for(int i=l;i<=R[bel[l]];i++){
b[i]+=c;
}
for(int i=L[bel[l]];i<=R[bel[l]];i++){
tmp[i]=b[i];
}
sort(tmp+L[bel[l]],tmp+R[bel[l]]+1);
for(int i=L[bel[l]];i<=R[bel[l]];i++){
a[i]=tmp[i];
}
for(int i=L[bel[r]];i<=r;i++){
b[i]+=c;
}
for(int i=L[bel[r]];i<=R[bel[r]];i++){
tmp[i]=b[i];
}
sort(tmp+L[bel[r]],tmp+R[bel[r]]+1);
for(int i=L[bel[r]];i<=R[bel[r]];i++){
a[i]=tmp[i];
}
for(int i=bel[l]+1;i<bel[r];i++){
tag[i]+=c;
}
}
}
void calc(int l,int r,ll c){
ll ans=-1e9;
bool fg=0;
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
if(b[i]+tag[bel[l]]<c)ans=max(ans,b[i]+tag[bel[l]]),fg=1;
}
}
else{
for(int i=l;i<=R[bel[l]];i++){
if(b[i]+tag[bel[l]]<c)ans=max(ans,b[i]+tag[bel[l]]),fg=1;
}
for(int i=L[bel[r]];i<=r;i++){
if(b[i]+tag[bel[r]]<c)ans=max(ans,b[i]+tag[bel[r]]),fg=1;
}
for(int i=bel[l]+1;i<bel[r];i++){
int p=lower_bound(a+L[i],a+R[i]+1,c-tag[i])-a-1;
if(a[p]+tag[i]<c)ans=max(ans,a[p]+tag[i]),fg=1;
}
}
if(fg)cout<<ans<<endl;
else cout<<-1<<endl;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],b[i]=a[i];
build(n);
for(int i=1;i<=n;i++){
ll opt,l,r,c;
cin>>opt>>l>>r>>c;
if(!opt){
update(l,r,c);
}
else{
calc(l,r,c);
}
}
return 0;
}

#6280. 数列分块入门 4 - 题目 - LibreOJ (loj.ac)

区间加法,区间求和(经典线段树,不过也可以分块)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=(1e5)+5;
ll a[maxn];
int L[maxn],R[maxn];
int bel[maxn];
ll siz[maxn];
ll tag[maxn];
void build(int n){
int block=sqrt(n);
int num=n/block;
if(n%block)num++;
for(int i=1;i<=num;i++){
L[i]=(i-1)*block+1;
R[i]=min(n,i*block);
for(int j=L[i];j<=R[i];j++){
bel[j]=i;
siz[i]+=a[j];
}
}
}
void update(int l,int r,ll c){
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
a[i]+=c;
siz[bel[l]]+=c;
}
}
else{
for(int i=l;i<=R[bel[l]];i++){
a[i]+=c;
siz[bel[l]]+=c;
}
for(int i=L[bel[r]];i<=r;i++){
a[i]+=c;
siz[bel[r]]+=c;
}
for(int i=bel[l]+1;i<bel[r];i++){
siz[i]+=(R[i]-L[i]+1)*c;
tag[i]+=c;
}
}
}
void calc(int l,int r,ll c){
ll ans=0;
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++){
ans=(ans+a[i]+tag[bel[l]])%c;
}
}
else{
for(int i=l;i<=R[bel[l]];i++){
ans=(ans+a[i]+tag[bel[l]])%c;
}
for(int i=L[bel[r]];i<=r;i++){
ans=(ans+a[i]+tag[bel[r]])%c;
}
for(int i=bel[l]+1;i<bel[r];i++){
ans=(ans+siz[i])%c;
}
}
cout<<ans%c<<endl;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
build(n);
for(int i=1;i<=n;i++){
ll opt,l,r,c;
cin>>opt>>l>>r>>c;
if(!opt){
update(l,r,c);
}
else calc(l,r,c+1);
}
return 0;
}

最新文章

  1. PHP-自定义模板-学习笔记
  2. Ctrip Mydream
  3. tyvj1195 最后的晚餐
  4. NOI 1.5 44:第n小的质数
  5. ARM-ContexM3/4组优先级和子优先级抢占规则
  6. Java设计模式10:观察者模式
  7. hdu 4751(dfs染色)
  8. mysql与mysqld
  9. PHP Filter
  10. ASP.NET中读取excel内容并显示
  11. android手机SD卡中的android_secure目录
  12. React Redux学习笔记
  13. 什么是&lt;!DOCTYPE html&gt;
  14. [Swift]LeetCode1033. 移动石子直到连续 | Moving Stones Until Consecutive
  15. 使用WebStorm报错 Namespace &#39;v-bind&#39; is not bound
  16. 类的反射及xml文件的解析
  17. 使用quartz数据库锁实现定时任务的分布式部署
  18. mysql连接数设置操作(Too many connections)及设置md5值的加密密码
  19. Django学习手册 - 自定义simple_tag / filter
  20. BZOJ.4555.[HEOI2016&amp;TJOI2016]求和(NTT 斯特林数)

热门文章

  1. mybatis_01 初运行
  2. 将含两列的csv文件生成二维矩阵
  3. mysql 获取某个时间段内每天的数据
  4. 指针和指针运算符一起时的运算规则(比如*p++和*++p的区别)
  5. Docker宿主机agetty进程cpu占用率100% 问题
  6. What is REST and Restful?
  7. SpringBoot2.2.2+SpringCloud-Hoxton.SR1整合eureka/gateway
  8. 1.win10安装centos虚拟机并设置允许远程
  9. ICPC2020上海B - Mine Sweeper II
  10. WPFprism框架