1. 程式人生 > 實用技巧 >[SHOI2015]腦洞治療儀 題解 (線段樹)

[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; }