题解 USACO08FEB 【Hotel G】

比较显然的线段树套路题。

考虑用线段树维护七个值,区间 \([l,r]\),区间长度 \(len\),区间内最大的连续空位 \(sum\),从左边最大延伸的长度 \(lmax\),从右边最大延伸的长度 \(ramx\),以及懒标记 \(lazytag\) \((\) \(1\)为开房,\(2\)为退房 \()\)

接下来下传懒标记。若开房,则区间没有剩余房间。若退房,则区间为空。

现在考虑 \(push\_up\),若左区间全为空房,则有转移

\[tree[x].lmax=tree[x \times 2].len+tree[x \times 2+1].lmax \]

否则父亲节点的 \(lmax\) 得到左子节点的 \(lmax\)

那么转移 \(ramx\) 同理。

然后不难发现父亲节点的最长区间可为左子节点的最长区间,右子节点的最长区间,以及跨越左右的区间。则有转移:

\[tree[x].sum=\max(tree[x\times 2].sum,tree[x\times 2+1].sum, tree[x \times 2].rmax+tree[x \times 2+1].lmax) \]

到此线段树的基本操作已经完成,对于区间修改,我们在线段树上支持开房和退房两种操作。

最后查询在 \([1,n]\) 中找到一个最前的空区间,满足此区间的长度 \(\geq x\),并返回左端点。

注意查询时按照顺序,即左子节点,左右合并,右子节点,找不到返回 \(0\)

\(\operatorname{Code}:\)

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
struct node {
	int l,r,len,lazytag;
	int sum,lmax,rmax;
} tree[N<<2]; //线段树 4 倍空间 
void build(int x,int l,int r) {
	tree[x].lazytag=0; //懒标记清零 
    tree[x].l=l, tree[x].r=r, tree[x].len=tree[x].r-tree[x].l+1; //区间左右端点以及区间长度 
	tree[x].sum=tree[x].lmax=tree[x].rmax=tree[x].len; //初始均为空房,所以连续空房长度都整个区间长度
	if(l==r) return; 
	int mid=(l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
}
void push_down(int x) { //下传懒标记 
	if(tree[x].lazytag==0) return; //无标记则返回
	if(tree[x].lazytag==1) { //如果要新开房 
		tree[x<<1].sum=tree[x<<1].lmax=tree[x<<1].rmax=0;
		tree[x<<1|1].sum=tree[x<<1|1].lmax=tree[x<<1|1].rmax=0; //这一段没有剩余房间 
		tree[x<<1].lazytag=tree[x<<1|1].lazytag=1; //下传懒标记 
	} else { //如果要退房 
		tree[x<<1].sum=tree[x<<1].lmax=tree[x<<1].rmax=tree[x<<1].len;	
		tree[x<<1|1].sum=tree[x<<1|1].lmax=tree[x<<1|1].rmax=tree[x<<1|1].len; //这一段都是空的		
		tree[x<<1].lazytag=tree[x<<1|1].lazytag=2; //下传懒标记 
	}
	tree[x].lazytag=0; //懒标记清零 
}
void push_up(int x) { //上传更新 
	if(tree[x<<1].sum == tree[x<<1].len) tree[x].lmax=tree[x<<1].len+tree[x<<1|1].lmax; //左区间为空房 
	else tree[x].lmax=tree[x<<1].lmax;
	if(tree[x<<1|1].sum == tree[x<<1|1].len) tree[x].rmax=tree[x<<1].rmax+tree[x<<1|1].len; //右区间为空房 
	else tree[x].rmax=tree[x<<1|1].rmax;
	tree[x].sum=max( max(tree[x<<1].sum,tree[x<<1|1].sum), tree[x<<1].rmax+tree[x<<1|1].lmax );  
}
void change(int x,int l,int r,int tag) {
	if(l<=tree[x].l && tree[x].r<=r) { //修改的区间覆盖当前区间 
		if(tag==1) tree[x].sum=tree[x].lmax=tree[x].rmax=0; //这一段没有剩余房间 
		else tree[x].sum=tree[x].lmax=tree[x].rmax=tree[x].len; //这一段都为空	
		tree[x].lazytag=tag; //下传懒标记 
		return;
	}
	push_down(x);
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid) change(x<<1,l,r,tag);
	if(r>mid) change(x<<1|1,l,r,tag);
	push_up(x);
}
int ask(int x,int l,int r,int lengh) {
	push_down(x);
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(tree[x<<1].sum>=lengh) return ask(x<<1,l,mid,lengh); //左区间里找 
	if(tree[x<<1].rmax+tree[x<<1|1].lmax>=lengh) return mid-tree[x<<1].rmax+1; 
	//左右儿子合并后满足就用中间
	if(tree[x<<1|1].sum>=lengh) return ask(x<<1|1,mid+1,r,lengh); //右区间里找 
	return 0; //找不到则返回 0 
}
int main() {
    int n,m; scanf("%d%d",&n,&m);
    build(1,1,n);
    while(m--) {
    	int opt; scanf("%d",&opt);
    	if(opt==1) {
    		int x; scanf("%d",&x);
			int Left=ask(1,1,n,x);
    		printf("%d\n",Left);
    		if(Left>0) change(1,Left,Left+x-1,1);
		} else {
			int x,y; scanf("%d%d",&x,&y);
			change(1,x,x+y-1,2);
		}
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/Agonim/p/12813240.html
今日推荐