【题目】
题目描述:
曾经发明了自动刷题机的发明家 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 n n、 m m m,表示 SHTSC 的大脑可分为从 1 1 1 到 n n n 编号的 n n n 个连续区域,有 m m m 个操作。
以下 m m m 行每行是下列三种格式之一:
0 0 0 l l l r r r:SHTSC 挖了一个范围为 [ l l l , r r r ] 的脑洞。
1 1 1 l 0 l_0 l0 r 0 r_0 r0 l 1 l_1 l1 r 1 r_1 r1:SHTSC 进行了一次脑洞治疗,用从 l 0 l_0 l0 到 r 0 r_0 r0 的脑组织修补 l 1 l_1 l1 到 r 1 r_1 r1 的脑洞。
2 2 2 l l l r r r :SHTSC 询问 [ l l l , r r r ] 区间内最大的脑洞有多大。
上述区间均在 [ 1 1 1 , n n n ] 范围内。
输出格式:
对于每个询问,输出一行一个整数,表示询问区间内最大连续脑洞区域有多大。
样例数据:
输入
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
备注:
【数据范围】
对于
20
%
20\%
20% 的数据,
n
,
m
n,m
n,m ≤
100
100
100
对于
50
%
50\%
50% 的数据,
n
,
m
n,m
n,m ≤
20000
20000
20000
对于
100
%
100\%
100% 的数据,
n
,
m
n,m
n,m ≤
200000
200000
200000
【分析】
终于把这道题弄出来了。。。
在三个操作中, 0 0 0 是区间赋值, 2 2 2 是求区间最长连续子段,应该都是线段树基本操作
对于稍微麻烦一点的 1 1 1,我们先查询 [ l 0 l_0 l0 , r 0 r_0 r0 ] 内 1 1 1 的个数,然后将整个区间赋成 0 0 0(这些也是基本操作),然后对于 [ l 1 l_1 l1 , r 1 r_1 r1 ],由于脑洞治疗仪仅会尽量填补位置比较靠前的脑洞,所以递归修改的时候就尽量先往左子树修改,如果还有剩余才修改右儿子(网上大部分题解说的二分也应该就是这个意思吧)
然后剩下的就直接套模板就行了
【代码】
#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;
}