cf102012E. Rikka with Data Structures

题目描述
题解

楼房重建的提高版。

用线段树维护每个区间的单调不下降的元素个数。

我们可以考虑假设左区间和右区间的个数已经知道了,现在要合并。

所以要用左区间的最大值 v v v 来计算右区间能加进来的个数。

于是递归右区间,如果其左区间的最大值小于 v v v ,那就递归右区间,否则递归左区间再加上右区间的个数即可。

右区间的个数就是区间个数-左区间个数。

效率: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码
#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值