教主的魔法
分块模板题
每一个M操作就是区间修改+暴力修改,每一个A操作就是区间查询,(A正解能用主席树,但是分析数据范围之后发现分块就够了)。
分析之后发现M操作就是分块基本操作,直接按照整区间内的数直接整块修改,不是整区间内的数暴力修改(也就是分块最重要的思想)来操作即可。
但是看到A操作发现直接查询时间复杂度会炸掉,于是考虑如何优化。发现如果区间内每个数都是有序的那么查询直接二分即可,则想到再引入一个数组b进行有序化原数组的操作,对于每一次修改,都对b进行一次更新加重新排序操作,然后直接在b数组上查询即可。
#include <bits/stdc++.h>
using namespace std;
const int N=1000000+50;
int a[N],b[N],bas,n,t,st[N],ed[N],pos[N],add[N],q,L,R,X;
int ans;
char s[N];
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<1)+(cnt<<3)+(c^48);c=getchar();}
return cnt*f;
}
inline void prework(){
for(int i=1;i<=t;++i){st[i]=(i-1)*bas+1;ed[i]=i*bas;}ed[t]=n;
for(int i=1;i<=n;++i){pos[i]=(i-1)/bas+1;}
for(int i=1;i<=t;++i){
sort(b+st[i],b+ed[i]+1);
}
}
inline void change(int L,int R,int X){
int p=pos[L],q=pos[R];
if(p==q){
for(int j=L;j<=R;++j){a[j]+=X;}
for(int j=st[p];j<=ed[q];++j){b[j]=a[j];}
sort(b+st[p],b+ed[p]+1);
}
else{
//第一个整块之前
for(int j=L;j<=ed[p];++j){a[j]+=X;}
for(int j=st[p];j<=ed[p];++j){b[j]=a[j];}
sort(b+st[p],b+ed[p]+1);
//最后一个整块之后
for(int j=st[q];j<=R;++j){a[j]+=X;}
for(int j=st[q];j<=ed[q];++j){b[j]=a[j];}
sort(b+st[q],b+ed[q]+1);
//整块
for(int j=p+1;j<=q-1;++j){add[j]+=X;}
}
}
inline void query(int L,int R,int X){
ans=0;
int p=pos[L],q=pos[R];
if(p==q){
for(int j=L;j<=R;++j){
if(add[p]+a[j]>=X) ++ans;
}
printf("%d\n",ans);
return;
}
else{
for(int j=L;j<=ed[p];++j){if(add[p]+a[j]>=X) ++ans;}
for(int j=st[q];j<=R;++j){if(add[q]+a[j]>=X) ++ans;}
for(int j=p+1;j<=q-1;++j){
int ll=st[j],rr=ed[j],mid,res=0;
while(ll<=rr){
mid=(ll+rr)>>1;
if(b[mid]+add[j]>=X){
rr=mid-1,res=ed[j]-mid+1;
}
else ll=mid+1;
}
ans+=res;
}
printf("%d\n",ans);
return;
}
}
signed main(){
n=read();q=read();bas=sqrt(n);t=n/bas;if(n%bas) ++t;
for(int i=1;i<=n;++i){a[i]=b[i]=read();}
prework();
for(int i=1;i<=q;++i){
scanf("%s",s);
L=read(),R=read(),X=read();
if(s[0]=='M') change(L,R,X);
if(s[0]=='A') query(L,R,X);
}
return 0;
}