【SHOI 2015】腦洞治療儀
【題目】
題目描述:
曾經發明瞭自動刷題機的發明家 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
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 的問題:在大腦某個區間中最大的連續腦洞區域有多大。
輸入格式:
第一行兩個整數 、,表示 SHTSC 的大腦可分為從 到 編號的 個連續區域,有 個操作。
以下 行每行是下列三種格式之一:
:SHTSC 挖了一個範圍為 [ , ] 的腦洞。
:SHTSC 進行了一次腦洞治療,用從 到 的腦組織修補 到 的腦洞。
:SHTSC 詢問 [ , ] 區間內最大的腦洞有多大。
上述區間均在 [ , ] 範圍內。
輸出格式:
對於每個詢問,輸出一行一個整數,表示詢問區間內最大連續腦洞區域有多大。
樣例資料:
輸入
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
輸出
3
3
6
6
備註:
【資料範圍】
對於 的資料, ≤
對於 的資料, ≤
對於 的資料, ≤
【分析】
終於把這道題弄出來了。。。
在三個操作中, 是區間賦值, 是求區間最長連續子段,應該都是線段樹基本操作
對於稍微麻煩一點的 ,我們先查詢 [ , ] 內 的個數,然後將整個區間賦成 (這些也是基本操作),然後對於 [ , ],由於腦洞治療儀僅會盡量填補位置比較靠前的腦洞,所以遞迴修改的時候就儘量先往左子樹修改,如果還有剩餘才修改右兒子(網上大部分題解說的二分也應該就是這個意思吧)
然後剩下的就直接套模板就行了
【程式碼】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
using namespace std;
int num,val[N];
struct Segment_Tree
{
int mark,len,sum,L,R,Max;
}a[N<<2];
Segment_Tree update(Segment_Tree left,Segment_Tree right)
{
Segment_Tree root;
root.mark=-1;
root.sum=left.sum+right.sum;
root.len=left.len+right.len;
root.L=(left.L==left.len)?left.L+right.L:left.L;
root.R=(right.R==right.len)?right.R+left.R:right.R;
root.Max=max(max(left.Max,right.Max),left.R+right.L);
return root;
}
void build(int root,int l,int r)
{
if(l==r)
{
a[root].mark=-1;
a[root].sum=a[root].len=1;
a[root].L=a[root].R=a[root].Max=0;
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
a[root]=update(a[root<<1],a[root<<1|1]);
}
void pushdown(int root,int l,int r,int mid)
{
a[root<<1].sum=a[root].mark*(mid-l+1);
a[root<<1|1].sum=a[root].mark*(r-mid);
a[root<<1].L=a[root<<1].R=a[root<<1].Max=(a[root].mark^1)*(mid-l+1);
a[root<<1|1].L=a[root<<1|1].R=a[root<<1|1].Max=(a[root].mark^1)*(r-mid);
a[root<<1].mark=a[root<<1|1].mark=a[root].mark;
a[root].mark=-1;
}
void modify(int root,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y)
{
a[root].mark=k;
a[root].sum=k*(r-l+1);
a[root].L=a[root].R=a[root].Max=(k^1)*(r-l+1);
return;
}
int mid=(l+r)>>1;
if(a[root].mark!=-1) pushdown(root,l,r,mid);
if(x<=mid) modify(root<<1,l,mid,x,y,k);
if(y>mid) modify(root<<1|1,mid+1,r,x,y,k);
a[root]=update(a[root<<1],a[root<<1|1]);
}
Segment_Tree query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return a[root];
int mid=(l+r)>>1;
if(a[root].mark!=-1) pushdown(root,l,r,mid);
if(y<=mid) return query(root<<1,l,mid,x,y);
if(x>mid) return query(root<<1|1,mid+1,r,x,y);
return update(query(root<<1,l,mid,x,y),query(root<<1|1,mid+1,r,x,y));
}
void change(int root,int l,int r,int x,int y)
{
if(!num) return;
if(l>=x&&r<=y&&num>=a[root].len-a[root].sum)
{
num-=a[root].len-a[root].sum;
a[root].mark=1,a[root].sum=r-l+1;
a[root].L=a[root].R=a[root].Max=0;
return;
}
int mid=(l+r)>>1;
if(a[root].mark!=-1) pushdown(root,l,r,mid);
if(x<=mid) change(root<<1,l,mid,x,y);
if(y>mid) change(root<<1|1,mid+1,r,x,y);
a[root]=update(a[root<<1],a[root<<1|1]);
}
int main()
{
int n,m,i;
scanf("%d%d",&n,&m);
build(1,1,n);
int s,x,y,l,r;
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&s,&x,&y);
if(s==0) modify(1,1,n,x,y,0);
else if(s==2) printf("%d\n",query(1,1,n,x,y).Max);
else
{
scanf("%d%d",&l,&r);
num=query(1,1,n,x,y).sum;
modify(1,1,n,x,y,0);
change(1,1,n,l,r);
}
}
return 0;
}