Title
Solution
注意lower_bound求的是大于等于,upper_bound求的是大于
之前还是比较纳闷有什么题目是分块做的了但是线段树做不了的,发现这道题目可以练一下分块
似乎洛谷上线段树的做法的都是可以被卡掉的。
但是听说: 线段树+分治似乎可以做。
可以重构(排序)每一个区间,以至于单调递增
这样的话,在询问的时候,可以二分处理。
时间复杂度
O
(
q
(
n
log
n
)
)
O(q\sqrt{(n\log n)} )
O(q(nlogn))
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#define rep(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
const int N=1e6+5,M=1e6+5;
int n,m,a[N],w,pos[N],L[M],R[M],b[N],add[M];
int read(){
int p=0; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) p=(p<<3)+(p<<1)+c-48,c=getchar();
return p;
}
void reset(int x){
rep(i,L[x],R[x]) b[i]=a[i];
sort(b+L[x],b+R[x]+1);
}
void change(int l,int r,int z){
int p=pos[l],q=pos[r];
if (p==q) {
rep(i,l,r) a[i]+=z;
} else {
rep(i,l,R[p]) a[i]+=z;
rep(i,L[q],r) a[i]+=z;
rep(i,p+1,q-1) add[i]+=z; reset(p),reset(q);
}
}
int ask_sum(int l,int r,int z){
int val=0;
rep(i,l,r) if (a[i]>=z) val++;
return val;
}
int find(int l,int r,int z){
int mid,rr=r;
while (l<=r){
mid=(l+r)>>1;
if (b[mid]<z) l=mid+1; else r=mid-1;
}
return rr-l+1;
}
// =upper_bound()> lower_bound()>= //maybe it has wrong
int ask(int l,int r,int z){
int p=pos[l],q=pos[r],val=0;
if (p==q) return ask_sum(l,r,z-add[p]); else {
rep(i,p+1,q-1) /*val+=R[i]-(upper_bound(b+L[i],b+R[i]+1,z)-b); */ val+=find(L[i],R[i],z-add[i]);
return val+ask_sum(l,R[p],z-add[p])+ask_sum(L[q],r,z-add[q]);
}
}
int main(){
n=read(),m=read();
rep(i,1,n) a[i]=read();
w=(int)sqrt(n);
rep(i,1,w) L[i]=R[i-1]+1,R[i]=i*w;
if (R[w]<n) w++,L[w]=R[w-1]+1,R[w]=n;
rep(i,1,w) {rep(j,L[i],R[i]) pos[j]=i; reset(i);}
while(m--){
char c;
cin>>c;
int l=read(),r=read(),z=read();
if (c=='M') change(l,r,z); else if (c=='A') printf("%d\n",ask(l,r,z));
}
return 0;
}