洛谷2801教主的魔法(分块)
刚学习了分块,写篇题解庆祝一下qwq
分块的核心思想是:把一段连续的区间分成若干块,这样当你询问的区间跨过了一块,原本 O ( n ) 的复杂度会降为 O ( 1 ) 或 O ( log ) ,其他零散的部分就暴力,这样就降低了一次询问的复杂度,于是就可以舒服的切掉线段树不能做的题了qwq
分块的建立代码:
void build(){//b=belong , le=length , k=块
le=sqrt(n);
k=n/le;
if(le*le<n) k++;
for(int i=1;i<=n;i++) b[i]=(i-1)/le+1,d[i]=a[i];
for(int i=1;i<=k;i++) l[i]=(i-1)*le+1,r[i]=i*le;
r[k]=n;
//接下来是一个块中需要存储的信息
}
接下来讲一讲洛谷2801这道傻逼分块题:
题目描述
教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。
每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)
CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。
WD巨懒,于是他把这个回答的任务交给了你。
输入输出格式
输入格式:
第1行为两个整数N、Q。Q为问题数与教主的施法数总和。
第2行有N个正整数,第i个数代表第i个英雄的身高。
第3到第Q+2行每行有一个操作:
(1) 若第一个字母为“M”,则紧接着有三个数字L、R、W。表示对闭区间 [L, R] 内所有英雄的身高加上W。
(2) 若第一个字母为“A”,则紧接着有三个数字L、R、C。询问闭区间 [L, R] 内有多少英雄的身高大于等于C。
输出格式:
对每个“A”询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。
输入输出样例
输入样例#1:
5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4
输出样例#1:
2
3
\(Solution:\)
对于每一块,你需要的只是将元素存到另一个d数组并对其排序
对于修改,多余的部分暴力,再重新放到d数组排一次序;中间部分,对每一块开一个p数组,表示这一块的所有元素应当加上的值,因为重新修改排序后不影响元素在块中位置,所以不必全部修改
对于询问,多余的部分暴力,中间部分因为有序,所以可以二分查找
贴代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1000010,M=1010;
inline int read(){
int f=1,w=0;char c=0;
while(!isdigit(c))
{
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) w=w*10+(c^48),c=getchar();
return f*w;
}
int n,m,k,le,l[M],r[M],a[N],b[N],d[N],p[M];
void build(){
le=sqrt(n);
k=n/le;
if(le*le<n) k++;
for(int i=1;i<=n;i++) b[i]=(i-1)/le+1,d[i]=a[i];
for(int i=1;i<=k;i++) l[i]=(i-1)*le+1,r[i]=i*le;
r[k]=n;
for(int i=1;i<=k;i++) sort(d+l[i],d+r[i]+1);
}
void change(int la,int lb,int v){
int ba=b[la],bb=b[lb];
if(ba==bb)
{
for(int i=la;i<=lb;i++) a[i]+=v;
for(int i=l[ba];i<=r[ba];i++) d[i]=a[i];
sort(d+l[la],d+r[lb]+1);
}
else
{
for(int i=la;i<=r[ba];i++) a[i]+=v;
for(int i=l[ba];i<=r[ba];i++) d[i]=a[i];
sort(d+l[ba],d+r[ba]+1);
for(int i=l[bb];i<=lb;i++) a[i]+=v;
for(int i=l[bb];i<=r[bb];i++) d[i]=a[i];
sort(d+l[bb],d+r[bb]+1);
for(int i=ba+1;i<=bb-1;i++) p[i]+=v;
}
}
int ask(int la,int lb,int c){
int ans=0,ba=b[la],bb=b[lb];
if(ba==bb)
{
for(int i=la;i<=lb;i++)
if(a[i]+p[ba]>=c) ans++;
}
else
{
for(int i=la;i<=r[ba];i++)
if(a[i]+p[ba]>=c) ans++;
for(int i=l[bb];i<=lb;i++)
if(a[i]+p[bb]>=c) ans++;
for(int i=ba+1;i<=bb-1;i++)
{
int ll=l[i],rr=r[i],res=0,mid;
while(ll<=rr)
{
mid=(ll+rr)>>1;
if(d[mid]+p[i]>=c)
rr=mid-1,res=r[i]-mid+1;
else ll=mid+1;
}
ans+=res;
}
}
return ans;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
build();
while(m--)
{
char c;cin>>c;
int x=read(),y=read(),z=read();
if(c=='A') printf("%d\n",ask(x,y,z));
else change(x,y,z);
}
return 0;
}