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
#include
#include
#include
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>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>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<