【SHOI2015】 脑洞治疗仪(线段树)

氵谷传送门

网上看到ODT吊打线段树的dalao。。。STO

但我不会ODT所以只能写线段树QAQ

三个操作分别对应:区间赋值为0,区间求1个数赋值为0再在指定区间从左往右填0,查询区间最长0。

第一个和第三个操作比较常规,难点在第二个。

一开始想的是直接统计1个数赋值,但可能被这种hack:

将第五个挖出放在前四个中,最后就应该是1 1 0 1 0,但错误做法会变成 1 1 1 1 0

所以单独写个modify修改,每次找区间零个数与挖出个数比较即可。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;

int n,q,ans,rsum;
struct Tree{
	int l,r,len;
	int lsum,rsum,mids,ans;
	int sum;
	int cov,num;
	friend inline Tree operator+(Tree a,Tree b){
		Tree c;
		c.lsum=(a.lsum==a.len)?(a.lsum+b.lsum):a.lsum;
		c.rsum=(b.rsum==b.len)?(b.rsum+a.rsum):b.rsum;
		c.mids=max(a.mids,max(b.mids,a.rsum+b.lsum));
		c.ans=max(c.lsum,max(c.rsum,c.mids));
	}
}tr[MAXN<<2];

int Read(){
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

#define lc (root<<1)
#define rc (root<<1|1)
#define mid ((l+r)>>1)
void push_up(int root){
	tr[root].sum=tr[lc].sum+tr[rc].sum;
	tr[root].lsum=(tr[lc].lsum==tr[lc].len)?(tr[lc].lsum+tr[rc].lsum):tr[lc].lsum;
	tr[root].rsum=(tr[rc].rsum==tr[rc].len)?(tr[rc].rsum+tr[lc].rsum):tr[rc].rsum;
	tr[root].mids=max(tr[lc].mids,max(tr[rc].mids,tr[lc].rsum+tr[rc].lsum));
	tr[root].mids=max(tr[root].mids,max(tr[root].lsum,tr[root].rsum));
}

void push_now(int root,int key){
	tr[root].cov=1;
	tr[root].num=key;
	tr[root].lsum=key?0:tr[root].len;
	tr[root].rsum=tr[root].mids=tr[root].lsum;
	tr[root].sum=key?tr[root].len:0;
}

void push_down(int root){
	if(tr[root].cov){
		push_now(lc,tr[root].num);
		push_now(rc,tr[root].num);
		tr[root].cov=0;
	}
}

void build(int root,int l,int r){
	tr[root].l=l,tr[root].r=r,tr[root].len=r-l+1;
	if(l==r){
		tr[root].sum=1;
		return ;
	}
	build(lc,l,mid);
	build(rc,mid+1,r);
	push_up(root);
}

void update(int root,int l,int r,int L,int R,int key){
	if(l>R||r<L)  return ;
	if(L<=l&&r<=R){
		push_now(root,key);
		return ;
	}
	push_down(root);
	if(R<=mid)
	  update(lc,l,mid,L,R,key);
	else{
		if(L>mid)
		  update(rc,mid+1,r,L,R,key);
		else
		  update(lc,l,mid,L,mid,key),update(rc,mid+1,r,mid+1,R,key);
	}
	push_up(root);
}

void modify(int root,int l,int r,int L,int R,int &total){
	if(!total)  return ;
	if(L<=l&&r<=R&&total>=tr[root].len-tr[root].sum){
		total-=tr[root].len-tr[root].sum;
		push_now(root,1);
		return ;
	}
	push_down(root);
	if(L<=mid)  modify(lc,l,mid,L,R,total);
	if(R>mid)  modify(rc,mid+1,r,L,R,total);
	push_up(root);
}

int query(int root,int l,int r,int L,int R){
	if(l>R||r<L)  return 0;
	if(L<=l&&r<=R)  return tr[root].sum;
	push_down(root);
	if(R<=mid)
	  return query(lc,l,mid,L,R);
	else{
		if(L>mid)
		  return query(rc,mid+1,r,L,R);
		else
		  return query(lc,l,mid,L,mid)+query(rc,mid+1,r,mid+1,R);
	}
}

void querymax(int root,int l,int r){
	if(l<=tr[root].l&&tr[root].r<=r){
		ans=max(ans,tr[root].mids);
		ans=max(ans,rsum+tr[root].lsum);
		if(tr[root].rsum==tr[root].len)  rsum+=tr[root].len;
		else  rsum=tr[root].rsum;
		return ;
	}
	push_down(root);
	int midd=tr[root].l+tr[root].r>>1;
	if(l<=midd)  querymax(lc,l,r);
	if(r>midd)  querymax(rc,l,r);
}

int main(){
	n=Read(),q=Read();
	build(1,1,n);
	while(q--){
		int cz=Read();
		int l=Read(),r=Read();
		if(cz==0)
		  update(1,1,n,l,r,0);
		if(cz==2)
		  ans=0,rsum=0,querymax(1,l,r),cout<<ans<<'\n';
		if(cz==1){
			int x=Read(),y=Read();
			int total=query(1,1,n,l,r);
			update(1,1,n,l,r,0);
			modify(1,1,n,x,y,total);
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值