【SHOI2015】脑洞治疗仪 题解 (线段树)
题目大意:给定一个只含$0$和$1$的序列。有三种操作:1.把$[l,r]$内所有数改为$0$;2.把$[l,r]$内所有$1$拿走来填$[l',r']$内所有$0$。多了丢掉,少了优先从左开始填;3.查询$[l,r]$内最长的$0$串。
---------------------------
一眼能看出来考最大子段和。但是代码好难调啊QAQ
对于一段序列,我们要维护$4$个东西:$ls,rs,ms,s$,分别为从左端点开始最长的$0$串,从右端点开始最长的$0$串,序列内最长的$0$串,序列和。
维护最大子段和,无非就两种情况:跨过$mid$和没有跨过$mid$。注意在维护$ms$的时候有三种情况:左儿子的$ms$,右儿子的$ms$,左儿子$rs$加右儿子$ls$。也可以翻我之前的博客。
对于区间修改我们维护一个$lazy$,在$pushdown$的时候维护一下序列就好。
最后说一下$2$操作。我们注意到在一段序列内的和是非严格单调递增的。所以要确定我们填的这个区间,我们只需二分一下这个右端点即可。
然后就是恶心人的调代码时间了。压过行后也有130行……
时间复杂度$O(n\log^2 n)$
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=200005; int n,m; struct node { int ms,ls,rs,s,lazy,l,r,len; }tree[maxn*4]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void pushup(int index) { tree[index].s=tree[index*2].s+tree[index*2+1].s; if (tree[index*2].len==tree[index*2].ls) tree[index].ls=tree[index*2].len+tree[index*2+1].ls; else tree[index].ls=tree[index*2].ls; if (tree[index*2+1].len==tree[index*2+1].rs) tree[index].rs=tree[index*2+1].len+tree[index*2].rs; else tree[index].rs=tree[index*2+1].rs; tree[index].ms=max(max(tree[index*2].ms,tree[index*2+1].ms),tree[index*2].rs+tree[index*2+1].ls); } inline void build(int index,int l,int r) { tree[index].l=l;tree[index].r=r; tree[index].len=r-l+1; tree[index].lazy=-1; if (l==r){tree[index].s=1;tree[index].ls=tree[index].rs=tree[index].ms=0;return;} int mid=(l+r)>>1; build(index*2,l,mid); build(index*2+1,mid+1,r); pushup(index); } inline void pushdown(int index,int l,int r)//ms,ls,rs,s,lazy { if (tree[index].lazy==0) { tree[index*2].lazy=tree[index*2+1].lazy=tree[index*2].s=tree[index*2+1].s=0; tree[index*2].ms=tree[index*2].ls=tree[index*2].rs=tree[index*2].len; tree[index*2+1].ms=tree[index*2+1].ls=tree[index*2+1].rs=tree[index*2+1].len; } if (tree[index].lazy==1) { tree[index*2].lazy=tree[index*2+1].lazy=1; tree[index*2].s=tree[index*2].len; tree[index*2+1].s=tree[index*2+1].len; tree[index*2].ms=tree[index*2].ls=tree[index*2].rs=0; tree[index*2+1].ms=tree[index*2+1].ls=tree[index*2+1].rs=0; } tree[index].lazy=-1; } inline void update(int index,int l,int r,int ql,int qr,int k) { if (ql<=l&&r<=qr) { tree[index].s=tree[index].len*k; if (k==0) tree[index].ls=tree[index].rs=tree[index].ms=tree[index].len,tree[index].lazy=0; else tree[index].ls=tree[index].rs=tree[index].ms=0,tree[index].lazy=1; return; } pushdown(index,l,r); int mid=(l+r)>>1; if (ql<=mid) update(index*2,l,mid,ql,qr,k); if (qr>mid) update(index*2+1,mid+1,r,ql,qr,k); pushup(index); } inline int querysum(int index,int l,int r,int ql,int qr) { if (ql<=l&&r<=qr) return tree[index].s; pushdown(index,l,r); int mid=(l+r)>>1,res=0; if (ql<=mid) res+=querysum(index*2,l,mid,ql,qr); if (qr>mid) res+=querysum(index*2+1,mid+1,r,ql,qr); return res; } inline int queryrest(int index,int l,int r,int ql,int qr) { if (ql<=l&&r<=qr) return tree[index].len-tree[index].s; pushdown(index,l,r); int mid=(l+r)>>1,res=0; if (ql<=mid) res+=queryrest(index*2,l,mid,ql,qr); if (qr>mid) res+=queryrest(index*2+1,mid+1,r,ql,qr); return res; } inline int querymax(int index,int l,int r,int ql,int qr) { if (ql<=l&&r<=qr) return tree[index].ms; pushdown(index,l,r); int mid=(l+r)>>1,res=0; if (tree[index*2].r<ql) return querymax(index*2+1,mid+1,r,ql,qr); else if (qr<tree[index*2+1].l) return querymax(index*2,l,mid,ql,qr); else{ res=max(querymax(index*2,l,mid,ql,qr),querymax(index*2+1,mid+1,r,ql,qr)); res=max(res,min(tree[index*2].rs,tree[index*2+1].l-ql)+min(tree[index*2+1].ls,qr-tree[index*2].r)); } return res; } inline void work(int l0,int r0) { int l1=read(),r1=read(); int x=querysum(1,1,n,l0,r0); if (x==0) return; update(1,1,n,l0,r0,0); int l=l1,r=r1,ans; while(l<=r){ int mid=(l+r)>>1; if(queryrest(1,1,n,l1,mid)<=x)l=mid+1,ans=mid; else r=mid-1; } update(1,1,n,l1,ans,1); } int main() { n=read(),m=read(); build(1,1,n); for (int i=1;i<=m;i++) { int opt=read(),l=read(),r=read(); if (opt==0) update(1,1,n,l,r,0); else if (opt==2) printf("%d\n",querymax(1,1,n,l,r)); else work(l,r); } return 0; }