题解
楼房重建的提高版。
用线段树维护每个区间的单调不下降的元素个数。
我们可以考虑假设左区间和右区间的个数已经知道了,现在要合并。
所以要用左区间的最大值 $v$ 来计算右区间能加进来的个数。
于是递归右区间,如果其左区间的最大值小于 $v$ ,那就递归右区间,否则递归左区间再加上右区间的个数即可。
右区间的个数就是区间个数-左区间个数。
效率: $O(nlog^2n)$ 。
代码
#include<bits/stdc++.h> #define LL long long using namespace std; const int N=1e5+5; int T,n,m; LL A[N]; struct O{int k,l,r;}; struct Tree{ LL a[N],t[N<<2],ad[N<<2],tg[N<<2]; int p[N<<2],c; O g[30]; #define Ls k<<1 #define Rs k<<1|1 #define mid ((l+r)>>1) void pushtg(int k,LL v,int l,int r){ t[k]=v;tg[k]=v;ad[k]=0;p[k]=r-l+1; } void pushad(int k,LL v){ t[k]+=v;ad[k]+=v; } void down(int k,int l,int r){ if (tg[k]) pushtg(Ls,tg[k],l,mid), pushtg(Rs,tg[k],mid+1,r),tg[k]=0; if (ad[k]) pushad(Ls,ad[k]), pushad(Rs,ad[k]),ad[k]=0; } int qry(int k,int l,int r,LL v){ if (l==r) return t[k]>=v; down(k,l,r); if (v>t[Ls]) return qry(Rs,mid+1,r,v); return qry(Ls,l,mid,v)+p[k]-p[Ls]; } void up(int k,int l,int r){ t[k]=max(t[Ls],t[Rs]); p[k]=p[Ls]+qry(Rs,mid+1,r,t[Ls]); } void build(int k,int l,int r){ ad[k]=tg[k]=0; if (l==r){t[k]=a[l];p[k]=1;return;} build(Ls,l,mid);build(Rs,mid+1,r);up(k,l,r); } void updad(int k,int l,int r,int L,int R,int v){ if (L<=l && r<=R) return pushad(k,v); down(k,l,r); if (mid>=L) updad(Ls,l,mid,L,R,v); if (mid<R) updad(Rs,mid+1,r,L,R,v); up(k,l,r); } void updtg(int k,int l,int r,int L,int R,int v){ if (L<=l && r<=R) return pushtg(k,v,l,r); down(k,l,r); if (mid>=L) updtg(Ls,l,mid,L,R,v); if (mid<R) updtg(Rs,mid+1,r,L,R,v); up(k,l,r); } void find(int k,int l,int r,int L,int R){ if (L<=l && r<=R){ g[++c]=(O){k,l,r}; return; } down(k,l,r); if (mid>=L) find(Ls,l,mid,L,R); if (mid<R) find(Rs,mid+1,r,L,R); } int get(int l,int r,LL v){ if (l>r) return 0; c=0;find(1,1,n,l,r); int u=0; for (int i=1;i<=c;i++) u+=qry(g[i].k,g[i].l,g[i].r,v), v=max(v,t[g[i].k]); return u; } LL qry(int k,int l,int r,int L,int R){ if (L<=l && r<=R) return t[k]; down(k,l,r); if (mid>=R) return qry(Ls,l,mid,L,R); if (mid<L) return qry(Rs,mid+1,r,L,R); return max(qry(Ls,l,mid,L,R),qry(Rs,mid+1,r,L,R)); } int ef(int k,int l,int r,int L,int R,LL v){ if (l==r) return l+(t[k]<=v); down(k,l,r); if (mid<L) return ef(Rs,mid+1,r,L,R,v); if (mid>=R) return ef(Ls,l,mid,L,R,v); if (qry(Ls,l,mid,L,mid)<=v) return ef(Rs,mid+1,r,L,R,v); return ef(Ls,l,mid,L,R,v); } int Get(int x,int l,int r){ if (l>r) return 0; LL w=qry(1,1,n,x,x); int u=get(x,r,w); int v=ef(1,1,n,l,r,w); int y=get(x,v-1,w); return u+v-l-y; } }t1,t2; void work(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%lld",&A[i]), t1.a[i]=A[i],t2.a[n-i+1]=A[i]; t1.build(1,1,n);t2.build(1,1,n); for (int op,l,r,x;m--;){ scanf("%d%d%d%d",&op,&l,&r,&x); if (op==1){ t1.updad(1,1,n,l,r,x); t2.updad(1,1,n,n-r+1,n-l+1,x); } else if (op==2){ t1.updtg(1,1,n,l,r,x); t2.updtg(1,1,n,n-r+1,n-l+1,x); } else{ if (x<l) printf("%d\n",t1.Get(x,x+1,r)-t1.Get(x,x+1,l-1)); else if (x>r) printf("%d\n",t2.Get(n-x+1,n-x+2,n-l+1)-t2.Get(n-x+1,n-x+2,n-r)); else printf("%d\n",t1.Get(x,x+1,r)+t2.Get(n-x+1,n-x+2,n-l+1)+1); } } } int main(){ for (scanf("%d",&T);T--;work()); return 0; }