#分块,二分#洛谷 3863 序列
分析
单点查询的话考虑将下标和时间调转,以下标为第一维,维护每一刻对应的值,
区间加就可以差分变成单点加,只不过是给一段后缀时间区间加,由于需要查询前缀时间
不妨考虑分块,整块修改打标记,散块修改归并,查询就是整块二分,散块暴力即可
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int N=100011; struct rec{int z,rk;}; vector<rec>K[N];
int n,m,a[N],bl,ans[N],v[N],rk[N],rK[N],Rk[N],pos[N],L[N],R[N]; long long lazy[N],c[N];
int iut(){
int ans=0,f=1; char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans*f;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
int main(){
n=iut(),m=iut(),bl=sqrt(m);
for (int i=1;i<=n;++i) a[i]=iut();
for (int i=0;i<=m;++i) pos[i]=i/bl;
for (int i=0;i<=m;++i) R[pos[i]]=i;
for (int i=m;~i;--i) L[pos[i]]=rk[i]=i;
for (int i=1;i<=m;++i)
if (iut()==1){
int l=iut(),r=iut(),z=iut();
K[l].push_back((rec){z,i});
if (r<n) K[r+1].push_back((rec){-z,i});
}else{
int x=iut(),z=iut();
K[x].push_back((rec){z,-i});
}
for (int x=1;x<=n;++x){
int siz=K[x].size();
for (int i=0;i<siz;++i)
if (K[x][i].rk<0){
K[x][i].rk*=-1;
int p=pos[K[x][i].rk-1],now=0;
for (int j=L[p];j<K[x][i].rk;++j)
if (a[x]+c[j]+lazy[p]>=K[x][i].z) ++now;
for (int j=0;j<p;++j){
int l=L[j],r=R[j]+1;
while (l<r){
int mid=(l+r)>>1;
if (a[x]+c[rk[mid]]+lazy[j]>=K[x][i].z) r=mid;
else l=mid+1;
}
now+=R[j]-r+1;
}
ans[K[x][i].rk]=now,v[K[x][i].rk]=1;
}else{
int p=pos[K[x][i].rk],i1=1,j1=1,o=L[p];
rK[0]=Rk[0]=0;
for (int j=L[p];j<=R[p];++j)
if (rk[j]>=K[x][i].rk) rK[++rK[0]]=rk[j],c[rk[j]]+=K[x][i].z;
else Rk[++Rk[0]]=rk[j];
while (i1<=rK[0]&&j1<=Rk[0]){
if (c[rK[i1]]<=c[Rk[j1]]) rk[o++]=rK[i1++];
else rk[o++]=Rk[j1++];
}
while (i1<=rK[0]) rk[o++]=rK[i1++];
while (j1<=Rk[0]) rk[o++]=Rk[j1++];
for (int j=pos[m];j>p;--j) lazy[j]+=K[x][i].z;
}
}
for (int i=1;i<=m;++i) if (v[i])
print(ans[i]),putchar(10);
return 0;
}