Description
曾經發明瞭自動刷題機的發明家SHTSC又公開了他的新發明:腦洞治療儀–一種可以治療他因爲發明而日益增大的腦洞的神祕裝置。
爲了簡單起見,我們將大腦視作一個01序列。1代表這個位置的腦組織正常工作,0代表這是一塊腦洞。
1 0 1 0 0 0 1 1 1 0
腦洞治療儀修補某一塊腦洞的基本工作原理就是將另一塊連續區域挖出,將其中正常工作的腦組織填補在這塊腦洞中。
(所以腦洞治療儀是腦洞的治療儀?)
例如,用上面第8號位置到第10號位置去修補第1號位置到第4號位置的腦洞。我們就會得到:
1 1 1 1 0 0 1 0 0 0
如果再用第1號位置到第4號位置去修補第8號位置到第10號位置:
0 0 0 0 0 0 1 1 1 1
這是因爲腦洞治療儀會把多餘出來的腦組織直接扔掉。
如果再用第7號位置到第10號位置去填補第1號位置到第6號位置:
1 1 1 1 0 0 0 0 0 0
這是因爲如果新腦洞挖出來的腦組織不夠多,腦洞治療儀僅會盡量填補位置比較靠前的腦洞。
假定初始時SHTSC並沒有腦洞,給出一些挖腦洞和腦洞治療的操作序列,你需要即時回答SHTSC的問題:
在大腦某個區間中最大的連續腦洞區域有多大。
Input
第一行兩個整數n,m。表示SHTSC的大腦可分爲從1到n編號的n個連續區域。有m個操作。
以下m行每行是下列三種格式之一。
0 l r :SHTSC挖了一個從l到r的腦洞。
1 l0 r0 l1 r2 :SHTSC進行了一次腦洞治療,用從l0到r0的腦組織修補l1到r1的腦洞。
2 l r :SHTSC詢問l到r這段區間最大的腦洞有多大。
n,m <=200000,1<=l<=r<=n
Output
對於每個詢問,輸出一行一個整數,表示詢問區間內最大連續腦洞區域有多大。
線段樹維護區間信息:
區間從左開始連續的0個數
區間從右開始連續的0個數
區間連續0的最大長度
區間0的個數
區間長度
區間被0/1覆蓋的標記
支持操作:
區間覆蓋0/1
查詢區間1的個數
查詢區間最長的連續0的長度
Solution
線段樹維護區間左右的連續0個數和區間內最大的連續0個數。
注意修補腦洞的時候如果左兒子能夠放下就遞歸左兒子,否則直接覆蓋左兒子,減去左兒子用掉的1的數量,遞歸右兒子。
代碼:
#include<cstdio>
#include<algorithm>
using namespace std;
template<typename T>inline void read(T &x){
T f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(x=0;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
x*=f;
}
const int maxn=200010;
struct Tag{
int lmax,rmax,rng,L,R;
Tag(){
lmax=rmax=rng=L=R=0;
}
friend Tag operator+(Tag lc,Tag rc){
Tag x;
x.lmax=lc.lmax+(lc.lmax==(lc.R-lc.L+1)?rc.lmax:0);
x.rmax=rc.rmax+(rc.rmax==(rc.R-rc.L+1)?lc.rmax:0);
x.rng=max(max(lc.rng,rc.rng),lc.rmax+rc.lmax);
x.L=lc.L;x.R=rc.R;
return x;
}
};
struct Segment_Tree{
#define lc x<<1
#define rc x<<1|1
int L[maxn<<2],R[maxn<<2],k;
int num[2][maxn<<2],same[maxn<<2];
Tag tag[maxn<<2];
void update(int x){
num[0][x]=num[0][lc]+num[0][rc];
num[1][x]=num[1][lc]+num[1][rc];
tag[x]=tag[lc]+tag[rc];
}
void pushsame(int x,int t){
num[t][x]=R[x]-L[x]+1;
num[t^1][x]=0;same[x]=t;
if(!t)tag[x].lmax=tag[x].rmax=tag[x].rng=num[0][x];
else tag[x].lmax=tag[x].rmax=tag[x].rng=0;
}
void Build(int x,int l,int r){
same[x]=-1;
if((tag[x].L=L[x]=l)==(tag[x].R=R[x]=r)){
pushsame(x,1);
return;
}
int mid=(l+r)>>1;
Build(lc,l,mid);Build(rc,mid+1,r);
update(x);
}
void pushdown(int x){
if(same[x]!=-1){
pushsame(lc,same[x]);
pushsame(rc,same[x]);
same[x]=-1;
}
}
void Modify(int x,int l,int r){
if(L[x]>=l&&R[x]<=r)return pushsame(x,0),void();
pushdown(x);
int mid=(L[x]+R[x])>>1;
if(l<=mid)Modify(lc,l,r);
if(r>mid)Modify(rc,l,r);
update(x);
}
void Treat(int x,int l,int r){
if(!k||!num[0][x])return;
if(L[x]>=l&&R[x]<=r){
if(num[0][x]<=k)return k-=num[0][x],pushsame(x,1),void();
pushdown(x);
if(k>num[0][lc]){
Treat(lc,l,r);
Treat(rc,l,r);
}
else Treat(lc,l,r);
update(x);
return;
}
pushdown(x);
int mid=(L[x]+R[x])>>1;
if(l<=mid)Treat(lc,l,r);
if(r>mid)Treat(rc,l,r);
update(x);
}
int Query(int x,int l,int r,int t){
if(L[x]>=l&&R[x]<=r)return num[t][x];
pushdown(x);
int mid=(L[x]+R[x])>>1,ans=0;
if(l<=mid)ans+=Query(lc,l,r,t);
if(r>mid)ans+=Query(rc,l,r,t);
return ans;
}
Tag Query(int x,int l,int r){
if(L[x]>=l&&R[x]<=r)return tag[x];
pushdown(x);
int mid=(L[x]+R[x])>>1;
if(l<=mid&&r>mid)return Query(lc,l,r)+Query(rc,l,r);
else if(l<=mid)return Query(lc,l,r);
else return Query(rc,l,r);
}
}tree;
int n,m;
int main(){
read(n);read(m);
tree.Build(1,1,n);
while(m--){
int opt,l0,r0,l1,r1,k;
read(opt);read(l0);read(r0);
if(!opt)tree.Modify(1,l0,r0);
else if(opt==1){
read(l1);read(r1);
k=tree.Query(1,l0,r0,1);
tree.Modify(1,l0,r0);
tree.k=k;
tree.Treat(1,l1,r1);
}
else{
Tag temp=tree.Query(1,l0,r0);
printf("%d\n",temp.rng);
}
}
return 0;
}