【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 号位置到第 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 rSHTSC 挖了一个范围为 [ 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 r1SHTSC 进行了一次脑洞治疗,用从 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 rSHTSC 询问 [ 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值