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;
}
Written by Alan_Zhao