[SHOI2015] 腦洞治療儀
題目描述
曾經發明瞭自動刷題機的發明家 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 的問題:在大腦某個區間中最大的連續腦洞區域有多大。
輸入格式
第一行兩個整數 \(n,m\),表示 SHTSC 的大腦可分為從 \(1\) 到 \(n\) 編號的 \(n\) 個連續區域,有 \(m\) 個操作。
以下 \(m\) 行每行是下列三種格式之一:
- \(0\quad l\quad r\):SHTSC 挖了一個範圍為 \([l, r]\) 的腦洞。
- \(1\quad l_0\quad r_0\quad l_1\quad r_1\):SHTSC 進行了一次腦洞治療,用從 \(l_0\) 到 \(r_0\) 的腦組織修補 \(l_1\) 到 \(r_1\) 的腦洞。
- \(2\quad l\quad r\):SHTSC 詢問 \([l, r]\) 區間內最大的腦洞有多大。
上述區間均在 \([1, n]\) 範圍內。
輸出格式
對於每個詢問,輸出一行一個整數,表示詢問區間內最大連續腦洞區域有多大。
樣例 #1
樣例輸入 #1
10 10
0 2 2
0 4 6
0 10 10
2 1 10
1 8 10 1 4
2 1 10
1 1 4 8 10
2 1 10
1 7 10 1 6
2 1 10
樣例輸出 #1
3
3
6
6
提示
對於 \(20\%\) 的資料,\(n, m \leq 100\);
對於 \(50\%\) 的資料,\(n, m \leq 20000\);
對於 \(100\%\) 的資料,\(n, m \leq 200000\)。
分析
我們發現,本題中的操作較為簡單:
操作一:區間覆蓋——區間改為0。
操作二:區間查詢+區間覆蓋——先查詢 $ [l0,r0] $ 1的個數\(num\),在 $ [l1,r1] $ 中二分找到第一個能把 $num $ 個1全部填入的末尾位置,區間改為1。
操作三:區間查詢——仿照最大子段和,我們定義 $ s[i].mxval $ 為最長的0區間,$ s[i].lsum $ 為從區間左端開始的最長0區間,$ s[i].rsum $ 同理,合併一下即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
inline int read()
{
register char c=getchar();
int x=0;
while(!isdigit(c))c=getchar();
while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return x;
}
int n,m;
struct segtree
{
int l,r,val;
int lsum,rsum,mxval;
int tag,siz;
}s[N<<2];
void build(int i,int l,int r)
{
s[i].l=l;s[i].r=r;
s[i].siz=s[i].val=r-l+1;
s[i].tag=-1;
if(l==r)return ;
int mid=(l+r)>>1;
build(i<<1,l,mid);build(i<<1|1,mid+1,r);
}
inline void pushup(segtree &a,segtree &lc,segtree &rc)
{
a.val=lc.val+rc.val;
a.lsum=lc.lsum;
if(lc.val==0)a.lsum+=rc.lsum;
a.rsum=rc.rsum;
if(rc.val==0)a.rsum+=lc.rsum;
a.mxval=max( max( lc.mxval,rc.mxval ) ,
lc.rsum+rc.lsum);
}
inline void pushtag(int i,int z)
{
s[i].tag=z;
s[i].val=z*s[i].siz;
s[i].mxval=s[i].lsum=s[i].rsum=s[i].siz-s[i].val;
}
inline void pushdown(int i)
{
if(s[i].tag==-1)return ;
pushtag(i<<1,s[i].tag);
pushtag(i<<1|1,s[i].tag);
s[i].tag=-1;
}
inline void upd(int i,int x,int y,int z)//turn range[x,y] 0/1
{
if( (s[i].val==0 && z==0) || (s[i].val==s[i].siz && z==1) )return ;
if(s[i].l>=x && s[i].r<=y)
{
pushtag(i,z);
return ;
}
pushdown(i);
int mid=(s[i].l+s[i].r)>>1;
if(x<=mid)upd(i<<1,x,y,z);
if(y>mid)upd(i<<1|1,x,y,z);
pushup(s[i],s[i<<1],s[i<<1|1]);
}
inline int que(int i,int x,int y)//query the 1 in range[x,y]
{
if(s[i].val==0)return 0;
if(s[i].l>=x && s[i].r<=y)
return s[i].val;
pushdown(i);
int mid=(s[i].l+s[i].r)>>1,sum=0;
if(x<=mid)sum+=que(i<<1,x,y);
if(y>mid)sum+=que(i<<1|1,x,y);
return sum;
}
inline void eq(segtree &a,segtree &b)//a <-- b
{
a.val=b.val;
a.mxval=b.mxval;
a.lsum=b.lsum;
a.rsum=b.rsum;
}
segtree ask(int i,int x,int y)//query the longest 0 range in range[x,y]
{
if(s[i].l>=x && s[i].r<=y)
return s[i];
pushdown(i);
int mid=(s[i].l+s[i].r)>>1;
segtree lc,rc,sum;
bool f1=0,f2=0;
if(x<=mid){f1=1;lc=ask(i<<1,x,y);}
if(y>mid){f2=1;rc=ask(i<<1|1,x,y);}
if(f1 && f2)
pushup(sum,lc,rc);
else if(f1) eq(sum,lc);
else eq(sum,rc);
return sum;
}
void work()
{
n=read();m=read();
build(1,1,n);
while(m--)
{
int opt,l,r,x,y;
opt=read();l=read();r=read();
if(opt==1)//use [l,r] to fix [x,y]
{
x=read();y=read();
int all=que(1,l,r);
upd(1,l,r,0);
int L=x,R=y,mid=0,pos=-1;
while(L<=R)
{
mid=(L+R)>>1;
if(mid-x+1-que(1,x,mid)<=all)
pos=mid,L=mid+1;
else R=mid-1;
}
if(pos>=x)
upd(1,x,pos,1);
}
else if(opt==2)
{
int ans=ask(1,l,r).mxval;
printf("%d\n",ans);
}
else
upd(1,l,r,0);
}
}
int main()
{
work();
return 0;
}