分块
分块算法,下限低上限高,属于一块大内容。从什么单点修改,到单点查询,再到区间修改,区间查询,再到后来的什么插入删除一个值等等,都可以用分块来做。而分块的每一道题目,几乎都是一个模板,所以,学习分块的时间会比较长,之后的几天,都会有更新博客中的模板。
模板:区间加法,单点查值
代码:
#include
using namespace std;
const int N=5e4+5;
int n,t,now,l,r,w;
int a[N],add[N],pos[N],ll[N],rr[N];
inline void change(int l,int r,int w)
{
if (pos[l]==pos[r]) for (register int i=l; i<=r; ++i) a[i]+=w;
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i) add[i]+=w;
for (register int i=l; i<=rr[pos[l]]; ++i) a[i]+=w;
for (register int i=ll[pos[r]]; i<=r; ++i) a[i]+=w;
}
}
int main(){
scanf("%d",&n);
for (register int i=1; i<=n; ++i) scanf("%d",&a[i]);
t=(int)sqrt(n);
for (register int i=1; i<=t; ++i) ll[i]=(i-1)*t+1,rr[i]=i*t;
if (rr[t]
此题帮助我理解了分块思想,学会了分块代码结构。
模板:区间加法,区间求和。
代码:
#include
#define int long long
using namespace std;
const int N=5e4+5;
int n,t,opt,l,r,c;
int a[N],add[N],pos[N],ll[N],rr[N],sum[N],vec[N];
inline void change(int l,int r,int c)
{
if (pos[l]==pos[r]) for (register int i=l; i<=r; ++i) a[i]+=c,sum[pos[l]]+=c;
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i) add[i]+=c;
for (register int i=l; i<=rr[pos[l]]; ++i) a[i]+=c,sum[pos[l]]+=c;
for (register int i=ll[pos[r]]; i<=r; ++i) a[i]+=c,sum[pos[r]]+=c;
}
}
inline int query(int l,int r,int c)
{
int ans=0;
c++;
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i) ans=(ans+a[i]+add[pos[i]])%c;
return ans;
}
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i) ans=(ans+add[i]*vec[i]+sum[i])%c;
for (register int i=l; i<=rr[pos[l]]; ++i) ans=(ans+a[i]+add[pos[l]])%c;
for (register int i=ll[pos[r]]; i<=r; ++i) ans=(ans+a[i]+add[pos[r]])%c;
return ans;
}
}
signed main(){
scanf("%lld",&n);
for (register int i=1; i<=n; ++i) scanf("%lld",&a[i]);
t=(int)sqrt(n);
for (register int i=1; i<=t; ++i) ll[i]=(i-1)*t+1,rr[i]=i*t;
if (rr[t]
和上一题相比,稍复杂些即可。
模板:区间开方,区间求和
观察到一个性质:任何在int 范围内的非负数,最多开方5次就会<=1,就能AC此题。
代码:
#include
#define int long long
using namespace std;
const int N=5e4+5;
int n,t,opt,l,r,c;
int a[N],add[N],pos[N],ll[N],rr[N],sum[N];
inline void change(int l,int r)
{
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i) if (a[i]>1) a[i]=(int)sqrt(a[i]);
add[pos[l]]=0;
for (register int i=ll[pos[l]]; i<=rr[pos[l]]; ++i) add[pos[l]]+=a[i];
}
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i)
{
if (sum[i]==5)continue;
sum[i]++;
add[i]=0;
for (register int j=ll[i]; j<=rr[i]; ++j) a[j]=(int)sqrt(a[j]),add[i]+=a[j];
}
for (register int i=l; i<=rr[pos[l]]; ++i) if (a[i]>1) a[i]=(int)sqrt(a[i]);
add[pos[l]]=0;
for (register int i=ll[pos[l]]; i<=rr[pos[l]]; ++i) add[pos[l]]+=a[i];
for (register int i=ll[pos[r]]; i<=r; ++i) if (a[i]>1) a[i]=(int)sqrt(a[i]);
add[pos[r]]=0;
for (register int i=ll[pos[r]]; i<=rr[pos[r]]; ++i) add[pos[r]]+=a[i];
}
}
inline int query(int l,int r)
{
int ans=0;
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i) ans+=a[i];
return ans;
}
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i) ans+=add[i];
for (register int i=l; i<=rr[pos[l]]; ++i) ans+=a[i];
for (register int i=ll[pos[r]]; i<=r; ++i) ans+=a[i];
return ans;
}
}
signed main(){
scanf("%lld",&n);
for (register int i=1; i<=n; ++i) scanf("%lld",&a[i]);
t=(int)sqrt(n);
for (register int i=1; i<=t; ++i) ll[i]=(i-1)*t+1,rr[i]=i*t;
if (rr[t]
以上三题应该属于最低等级难度
下面两题要有一定简单的思考。
模板:区间加法,询问区间内小于某个值 的元素个数
代码:
#include
using namespace std;
const int N=5e4+5;
int n,t,now,opt,l,r,c,begin,end;
int a[N],add[N],ll[N],rr[N],pos[N];
int num[N],sum[N];
inline void change(int l,int r,int c)
{
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i) a[i]+=c;
for (register int i=ll[pos[l]]; i<=rr[pos[l]]; ++i) num[i]=a[i];
sort(num+ll[pos[l]],num+rr[pos[l]]+1);
}
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i) add[i]+=c;
for (register int i=l; i<=rr[pos[l]]; ++i) a[i]+=c;
for (register int i=ll[pos[l]]; i<=rr[pos[l]]; ++i) num[i]=a[i];
sort(num+ll[pos[l]],num+rr[pos[l]]+1);
for (register int i=ll[pos[r]]; i<=r; ++i) a[i]+=c;
for (register int i=ll[pos[r]]; i<=rr[pos[r]]; ++i) num[i]=a[i];
sort(num+ll[pos[r]],num+rr[pos[r]]+1);
}
}
inline int query(int l,int r,int c)
{
int ans=0;
c=c*c;
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i) if (a[i]+add[pos[i]]
模板:区间加法,询问区间内小于某个值 的前驱(比其小的最大元素)
代码:
#include
using namespace std;
const int N=1e5+5;
int n,t,now,opt,l,r,c,begin,end;
int a[N],add[N],ll[N],rr[N],pos[N];
int num[N],sum[N];
inline void change(int l,int r,int c)
{
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i) a[i]+=c;
for (register int i=ll[pos[l]]; i<=rr[pos[l]]; ++i) num[i]=a[i];
sort(num+ll[pos[l]],num+rr[pos[l]]+1);
}
else
{
for (register int i=pos[l]+1; i<=pos[r]-1; ++i) add[i]+=c;
for (register int i=l; i<=rr[pos[l]]; ++i) a[i]+=c;
for (register int i=ll[pos[l]]; i<=rr[pos[l]]; ++i) num[i]=a[i];
sort(num+ll[pos[l]],num+rr[pos[l]]+1);
for (register int i=ll[pos[r]]; i<=r; ++i) a[i]+=c;
for (register int i=ll[pos[r]]; i<=rr[pos[r]]; ++i) num[i]=a[i];
sort(num+ll[pos[r]],num+rr[pos[r]]+1);
}
}
inline int query(int l,int r,int c)
{
int MAX=100000000,ans=-1,poss;
if (pos[l]==pos[r])
{
for (register int i=l; i<=r; ++i)
if (a[i]+add[pos[i]]=c-add[i]) continue;
poss=(lower_bound(num+ll[i],num+rr[i]+1,c-add[i])-num)-1;
if (c-(num[poss]+add[i])