Loading

P4344 [SHOI2015] 脑洞治疗仪

题解

简单题。我用的是没有均摊的做法。

对于操作 \(2\)(用 \([l_0,r_0]\) 修补 \([l_1,r_1]\)),我们可以求出 \([l_0,r_0]\) 之间有多少个 \(1\),记这个个数为 \(s\),然后将这一段清零,再在 \([l_1,r_1]\) 中找到位置 \(p\) 使得 \([l_1,p]\)\(0\) 的个数 \(=k\)。那么我们就会用 \(s\)\(1\) 恰好填满 \([l_1,p]\) 中的所有 \(0\),于是这也是一个区间赋值操作。

于是我们需要支持:

  • 区间赋值;
  • 查询区间中 \(0/1\) 的个数;
  • 查询区间中最长的 \(0\) 的连续段的大小;
  • 在区间 \([l,r]\) 中找到一个位置 \(p\) 使得 \([l,p]\) 中有恰好 \(k\)\(0\)

我们在线段树上记录区间的:

  • \(0\) 的个数;
  • 左、右端点处的值;
  • 最长连续的 \(0\) 的个数;
  • 最长前、后缀 \(0\) 的个数;

就做完了。时间复杂度 \(\mathcal{O}(n+m\log n)\)

省队集训的时候学会了如何在一个特定区间内线段树二分。我改进了一下线段树二分的写法,减少了约 \(\frac{1}{3}\) 的常数和代码量。大概主要是因为当时的写法太蠢了?

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
typedef long long ll;
const int N=2e5+5,Inf=0x3f3f3f3f;
struct SegmentTree{
	struct Node{
		int l,r,cnt0,lcol=1,rcol=1,lsiz,rsiz,mx,Emp=-Inf;
		Node operator+(const Node &rs) const{
			if(!l) return rs;
			if(!rs.l) return *this;
			int Mx=max({mx,rs.mx,(!rcol&&!rs.lcol)*(rsiz+rs.lsiz)});
			return {l,rs.r,cnt0+rs.cnt0,lcol,rs.rcol,(lsiz==r-l+1?rs.lsiz:0)+lsiz,(rs.rsiz==rs.r-rs.l+1?rsiz:0)+rs.rsiz,Mx};
		}
		void PushEmp(int k){
			if(k) cnt0=lsiz=rsiz=mx=0;
			else cnt0=lsiz=rsiz=mx=r-l+1;
			lcol=rcol=Emp=k;
		}
	}t[N<<2];
	void Pushdown(int p){
		if(t[p].Emp!=-Inf) t[p*2].PushEmp(t[p].Emp),t[p*2+1].PushEmp(t[p].Emp),t[p].Emp=-Inf;
	}
	void Build(int p,int l,int r){
		t[p].l=l,t[p].r=r;
		if(l==r) return;
		Build(p*2,l,(l+r)/2),Build(p*2+1,(l+r)/2+1,r);
		t[p]=t[p*2]+t[p*2+1];
	}
	int _Search(int p,int rem){
		if(t[p].l==t[p].r) return t[p].l;
		Pushdown(p);
		if(t[p*2].cnt0>=rem) return _Search(p*2,rem);
		return _Search(p*2+1,rem-t[p*2].cnt0);
	}
	int Search(int p,int l,int r,int val,int &ans){
		if(l>t[p].r||r<t[p].l) return 0;
		if(l<=t[p].l&&t[p].r<=r){
			if(val<=t[p].cnt0){ans=_Search(p,val);}
			return t[p].cnt0;
		}
		Pushdown(p);
		int res=Search(p*2,l,r,val,ans);
		if(res<val) res+=Search(p*2+1,l,r,val-res,ans);
		return res;
	}
	void Emplace(int p,int l,int r,int k){
		if(l<=t[p].l&&t[p].r<=r) return t[p].PushEmp(k);
		Pushdown(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) Emplace(p*2,l,r,k);
		if(r>mid) Emplace(p*2+1,l,r,k);
		t[p]=t[p*2]+t[p*2+1];
	}
	Node Query(int p,int l,int r){
		if(l>t[p].r||r<t[p].l) return Node();
		if(l<=t[p].l&&t[p].r<=r) return t[p];
		Pushdown(p);
		Node resl=Query(p*2,l,r),resr=Query(p*2+1,l,r);
		return resl+resr;
	}
}seg;
int n,m;
int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>n>>m;
	seg.Build(1,1,n);
	For($,1,m){
		int op,l,r,x,y;cin>>op>>l>>r;
		if(op==0) seg.Emplace(1,l,r,0);
		else if(op==1){
			cin>>x>>y;
			int s=r-l+1-seg.Query(1,l,r).cnt0;
			if(!s) continue;
			seg.Emplace(1,l,r,0);
			int ans=0;seg.Search(1,x,y,s,ans);
			if(!ans) ans=y;
			seg.Emplace(1,x,ans,1);
		}else cout<<seg.Query(1,l,r).mx<<'\n';
	}
	return 0;
}
posted @ 2021-12-11 21:23  Alan_Zhao_2007  阅读(52)  评论(0编辑  收藏  举报