概念
所谓“分块”,就是把一个序列分成一个个长度相等的块,那么对于一个长度为 的序列,我们一般可以将这个序列分成
个长度为
的块。
当然,对于某些毒瘤出题人,我们可以相应地将块长更改一下(例如 等)。
分块实现
那得先分块吧。
首先,至少会有 个块,但是,可能会有多余的出来,所以要多分一个块。
局部代码:
(len 为块长,cnt 为块的数量)
len=sqrt(n);
cnt=n/len;
if(n%len)
cnt++;
然后,对于第 个块,我们设
为它的左端点,
为它的右端点。
易得:。
特别的,。
局部代码:
for(int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
为了方便一些操作,我们令 表示原序列中的第
个数在哪个块中。
代码也很简单:
for(int i=1;i<=cnt;i++)
{
for(int j=l[i];j<=r[i];j++)
{
pos[j]=i;
}
}
就这样,块就分好了。
分块的基本操作
一、在序列的
内加上/减去 
先对区间两端的不完整的块进行暴力修改,
对于中间的块:
和线段树懒标记类似的,我们可以设 表示第
个块的懒标记(其实也不算),那么块中的每个数就应该是
二、查询序列中
的和
涉及到查询区间和,可以考虑先预处理出 (第
个块的和),那么上面的修改操作就需要对
做出修改。
修改操作如何修改 sum?
对于两端的块,暴力改 sum;
中间的块,就加上 乘以块长。
查询也是一样,累加 sum 即可。
三、查询序列
中有多少个数大于/小于(等于)
涉及到了大小关系,首先想到就是排序……
对块内排序(那么对于前面的修改操作,有对两端进行暴力修改,所以改后要对两端的块重新排序)。
对于两端的块:暴力枚举累计;
对于中间的块:二分找出满足条件的分界点,然后累计即可。
例题:教主的魔法
传送门https://www.luogu.com.cn/problem/P2801模板题,都是分块的基本操作……
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int a[1000001];
int b[1000001];
int len;
int cnt;
int l[10001],r[10001],pos[1000001];
int lazy[10001];
void init()
{
for(int i=1;i<=n;i++)
{
a[i]=b[i];
}
len=sqrt(n);
cnt=n/len;
if(n%len)
cnt++;
for(int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
for(int i=1;i<=cnt;i++)
{
for(int j=l[i];j<=r[i];j++)
{
pos[j]=i;
//cout<<i<<" "<<j<<endl;
}
}
// for(int i=1;i<=n;i++)
// {
// cout<<pos[i]<<" ";
// }
// cout<<endl;
for(int i=1;i<=cnt;i++)
{
sort(a+l[i],a+r[i]+1);
}
// for(int i=1;i<=n;i++)
// cout<<a[i]<<" ";
// cout<<endl;
}
void change(int x,int y,int d)
{
if(pos[x]==pos[y])
{
for(int i=x;i<=y;i++)
{
b[i]+=d;
}
for(int i=l[pos[x]];i<=r[pos[x]];i++)
{
a[i]=b[i];
}
sort(a+l[pos[x]],a+r[pos[x]]+1);
return;
}
for(int i=x;i<=r[pos[x]];i++)
{
b[i]+=d;
}
for(int i=l[pos[x]];i<=r[pos[x]];i++)
{
a[i]=b[i];
}
sort(a+l[pos[x]],a+r[pos[x]]+1);
for(int i=l[pos[y]];i<=y;i++)
{
b[i]+=d;
}
for(int i=l[pos[y]];i<=r[pos[y]];i++)
{
a[i]=b[i];
}
sort(a+l[pos[y]],a+r[pos[y]]+1);
for(int i=pos[x]+1;i<=pos[y]-1;i++)
{
lazy[i]+=d;
}
// cout<<r[pos[x]]<<" "<<l[pos[y]]<<endl;
// for(int i=1;i<=n;i++)
// cout<<a[i]<<" ";
// cout<<endl;
// for(int i=1;i<=n;i++)
// cout<<b[i]<<" ";
// cout<<endl;
}
int query(int x,int y,int c)
{
int res=0;
if(pos[x]==pos[y])
{
for(int i=x;i<=y;i++)
{
if(b[i]+lazy[pos[x]]>=c)res++;
}
return res;
}
for(int i=x;i<=r[pos[x]];i++)
{
// cout<<a[i]+lazy[pos[x]]<<" ";
if(b[i]+lazy[pos[x]]>=c)res++;
}
for(int i=l[pos[y]];i<=y;i++)
{
// cout<<a[i]+lazy[pos[y]]<<" ";
if(b[i]+lazy[pos[y]]>=c)res++;
}
// cout<<res<<endl;
int ll,rr,mid;
int temp;
for(int i=pos[x]+1;i<=pos[y]-1;i++)
{
ll=l[i],rr=r[i];
temp=0;
while(ll<=rr)
{
mid=(ll+rr)>>1;
if(a[mid]+lazy[i]>=c)
{
temp=r[i]-mid+1;
rr=mid-1;
}
else
ll=mid+1;
}
// cout<<i<<" "<<temp<<endl;
res+=temp;
}
// cout<<lazy[2]<<endl;
// for(int i=l[2];i<=r[2];i++)
// {
// cout<<a[i]<<" "<<b[i]<<endl;
// }
return res;
}
signed main()
{
// freopen("aa.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
init();
char ch;
int x,y,z;
while(q--)
{
cin>>ch;
cin>>x>>y>>z;
if(ch=='M')
{
change(x,y,z);
}
else{
cout<<query(x,y,z)<<"\n";
}
}
return 0;
}
最后,求赞勿喷