P4344 [SHOI2015] 脑洞治疗仪——线段树
[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;
}
本文来自博客园,作者:Glowingfire,转载请注明原文链接:https://www.cnblogs.com/Glowingfire/p/18490952