艰苦地刷了4天半的分块
深感分块是一个非常巧(暴)妙(力) 的算法
如果有觉得hzwer的代码太奇妙(看不懂)的推荐一下机房大佬的通俗易懂的代码:
http://www.cnblogs.com/CHerish_OI/category/1176577.html(此处手动艾特cherish_oi同学)
http://hzwer.com/8053.html
loj#6277. 数列分块入门 1
区间加法,单点查询
基础的东西
数学老师曰:基本知识不熟悉,难题不会做!
分块 每个块的大小都是sqrt(n)
n=12 N=sqrt(12) 向下取整 3
第一块:1~3 第二块:4~6 第三块:7~9 第四块: 10~12
属于第几块pos[i]=(i-1)/N+1;
如果是完整的块就直接更新到标记里
不完整的块就直接暴力更新就ok
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int m,pos[51000];
int s[51000],tag[51000];
void change(int a,int b,int c)
{
for (int i=a;i<=min(b,pos[a]*m);i++) s[i]+=c;//如果b在a的块内就更新到b 否则更新到a的块的结尾
if (pos[a]!=pos[b])
{
for (int i=(pos[b]-1)*m+1;i<=b;i++) s[i]+=c; //如果ab不同块 更新b的块内的开头到b
}
for (int i=pos[a]+1;i<=pos[b]-1;i++) tag[i]+=c;
}
int main()
{
int n;
scanf("%d",&n);
m=sqrt(n);
for (int i=1;i<=n;i++) pos[i]=(i-1)/m+1;
for (int i=1;i<=n;i++) scanf("%d",&s[i]);
for (int i=1;i<=n;i++)
{
int x,a,b,c;
scanf("%d%d%d%d",&x,&a,&b,&c);
if (x==0)
{
change(a,b,c);
}
else if (x==1)
{
printf("%d\n",tag[pos[b]]+s[b]);
}
}
return 0;
}
loj#6278. 数列分块入门 2
区间加法,查询比x小的个数
排序维护一下块内的递增性
完整的块可以直接用lower_bound返回一下第一个大于等于x的位置
不完整的块还是直接暴力
非常不争气的写了STL
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
int n,N;
int pos[51000],s[51000],tag[51000];
vector<int> v[510];
void update(int x)
{
v[x].clear();
for (int i=(x-1)*N+1;i<=min(x*N,n);i++)//有可能这是最后一个块 min一下
v[x].push_back(s[i]);//push_back:压进容器尾部
sort(v[x].begin(),v[x].end());//从容器头到容器尾排序
}
void add(int x,int y,int c)
{
for (int i=x;i<=min(pos[x]*N,y);i++)//x,y有可能同块 否则更新完x块剩下整个快
s[i]+=c;
update(pos[x]);
if (pos[x]!=pos[y])
{
for (int i=(pos[y]-1)*N+1;i<=y;i++) //更新y所在的不完整块
s[i]+=c;
update(pos[y]);
}
for (int i=pos[x]+1;i<pos[y];i++) tag[i]+=c;
}
int getsum(int x,int y,int c)//x~y中统计小于c个数
{
int ans=0;
for (int i=x;i<=min(pos[x]*N,y);i++)
if (s[i]+tag[pos[x]]<c) ans++; //x块
if (pos[x]!=pos[y])//y
{
for (int i=((pos[y]-1)*N+1);i<=y;i++) if (s[i]+tag[pos[y]]<c) ans++;
}
for (int i=pos[x]+1;i<pos[y];i++)
{
int t=c-tag[i];
ans+=lower_bound(v[i].begin(),v[i].end(),t)-v[i].begin();//lower_bound:返回x的后继(第一个比x大于等于的位置)(v内有序)
}
return ans;
}
int main()
{
scanf("%d",&n);
N=sqrt(n);
for (int i=1;i<=n;i++) scanf("%d",&s[i]);
for (int i=1;i<=n;i++)
{
pos[i]=(i-1)/N+1;
v[pos[i]].push_back(s[i]);
}
for (int i=1;i<=pos[n];i++)
sort(v[i].begin(),v[i].end());
for (int i=1;i<=n;i++)
{
int u,x,y,c;
scanf("%d%d%d%d",&u,&x,&y,&c);
if (u==0) add(x,y,c);
else if (u==1) printf("%d\n",getsum(x,y,c*c));
}
return 0;
}
loj#6279. 数列分块入门 3
区间加法,求某个数的前驱
用set维护方便很多(其实相当于一颗splay把我觉得,手动艾特lynkin同学用的二分查找前驱过了)
依旧是完整的块直接lower_bound
不完整的块还是继续暴力
不过要注意set的使用方法 删掉再进行暴力修改 再加回去
依旧不争气地继续用了STL
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
set<int> tr[1100];//set相当于一颗树
int s[110000],tag[110000],pos[110000];
int n,N;
void add(int x,int y,int c)
{
for (int i