[USACO13JAN]Seating G

洛谷题面

题目大意

有 \(n\) 个座位,\(m\) 次操作。

\(\rm A\) 操作:将 \(a\) 名客人安置到最左的连续 \(a\) 个空位中,没有则不操作。

\(\rm L\) 操作:\([a,b]\) 的客人离开。

求 \(\rm A\) 操作中所有不操作的次数。

题目分析

和 \(\verb!P2894!\) 很像。注意这道题 \(\rm L\) 操作是针对区间 \([l,r]\),\(\verb!P2894!\) 是针对区间 \([l,l+x-1]\)。这里坑了我两天。

定义 \(lmax\) 表示从左开始的最大连续空位数,从右开始的连续最大空位数,以及该区间的最大连续空位数 \(max\)。

之所以定义 \(lmax,rmax\),是因为我们从该区间扩展的时候不能只靠 \(max\),也就是说该区间的 \(max\) 不等于左儿子和右儿子的 \(max\) 之和。举个例子:

区间 \([1,8]\)(\(0\) 表示空位,\(1\) 表示非空位):0 0 0 1 0 0 0 1。\(max[1,8]\neq max[1,4]+max[5,8]\)。但是用 \(lmax,rmax\) 就可以解决了:

inline void pushup(int p)
{
	node[p].Max=max(node[ls(p)].Max,max(node[rs(p)].Max,node[ls(p)].rmax+node[rs(p)].lmax));

	node[p].lmax=node[ls(p)].Max==(node[ls(p)].r-node[ls(p)].l+1)?node[ls(p)].Max+node[rs(p)].lmax:node[ls(p)].lmax;

	node[p].rmax=node[rs(p)].Max==(node[rs(p)].r-node[rs(p)].l+1)?node[rs(p)].Max+node[ls(p)].rmax:node[rs(p)].rmax;
}

对于修改操作,我们仍然使用懒标记。\(tag=1\) 表示非空位,\(tag=0\) 表示空位。

于是可以这样:

inline void pushdown(int p)
{
	if(node[p].tag==0)
	{
		node[ls(p)].tag=node[rs(p)].tag=0;

		node[ls(p)].Max=node[ls(p)].lmax=node[ls(p)].rmax=node[ls(p)].r-node[ls(p)].l+1;

		node[rs(p)].Max=node[rs(p)].lmax=node[rs(p)].rmax=node[rs(p)].r-node[rs(p)].l+1;
	}

	else if(node[p].tag==1)
	{
		node[ls(p)].tag=node[rs(p)].tag=1;

		node[ls(p)].Max=node[ls(p)].lmax=node[ls(p)].rmax=0;

		node[rs(p)].Max=node[rs(p)].lmax=node[rs(p)].rmax=0;
	}

	node[p].tag=-1;
}

修改操作没啥好讲的,重点讲讲查询操作。

我们查询操作是为了返回左端点值,如果左儿子装得下 \(x\) 个座位,那么递归到左儿子去;如果跨越左右儿子的空位数装得下 \(x\) 个座位,那么返回左端点位置;否则递归到右儿子去。

inline int query(int p,int k)
{
	if(node[p].l==node[p].r)
	{
		return node[p].l;
	}

	Segment_Tree::pushdown(p);

	int mid=node[p].l+node[p].r>>1;

	if(node[ls(p)].Max>=k)
	{
		return Segment_Tree::query(ls(p),k);
	}

	if(node[ls(p)].rmax+node[rs(p)].lmax>=k)
	{
		return node[ls(p)].r-node[ls(p)].rmax+1;
	}

	else
	{
		return Segment_Tree::query(rs(p),k);
	}
}

代码

完整代码:

//2022/1/31

//2022/2/1

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

#include <cstdio>

#include <climits>//need "INT_MAX","INT_MIN"

#include <cstring>//need "memset"

#include <algorithm>

#define enter() putchar(10)

#define debug(c,que) cerr<<#c<<" = "<<c<<que

#define cek(c) puts(c)

#define blow(arr,st,ed,w) for(register int i=(st);i<=(ed);i++)cout<<arr[i]<<w;

#define speed_up() cin.tie(0),cout.tie(0)

#define endl "\n"

#define Input_Int(n,a) for(register int i=1;i<=n;i++)scanf("%d",a+i);

#define Input_Long(n,a) for(register long long i=1;i<=n;i++)scanf("%lld",a+i);

#define mst(a,k) memset(a,k,sizeof(a))

namespace Newstd
{
	inline int read()
	{
		int x=0,k=1;
		char ch=getchar();
		while(ch<'0' || ch>'9')
		{
			if(ch=='-')
			{
				k=-1;
			}
			ch=getchar();
		}
		while(ch>='0' && ch<='9')
		{
			x=(x<<1)+(x<<3)+ch-'0';
			ch=getchar();
		}
		return x*k;
	}
	inline void write(int x)
	{
		if(x<0)
		{
			putchar('-');
			x=-x;
		}
		if(x>9)
		{
			write(x/10);
		}
		putchar(x%10+'0');
	}
}

using namespace Newstd;

using namespace std;

const int ma=5e5+5;

struct Node
{
	int l,r;

	int lmax,rmax;//from left,from right

	int tag,Max;

	//if tag = 0: get out
	//if tag = 1: come in
};

Node node[ma<<2];

int n,m;

namespace Segment_Tree
{
	#define ls(p) (p<<1)

	#define rs(p) (p<<1|1)

	inline void pushup(int p)
	{
		node[p].Max=max(node[ls(p)].Max,max(node[rs(p)].Max,node[ls(p)].rmax+node[rs(p)].lmax));

		node[p].lmax=node[ls(p)].Max==(node[ls(p)].r-node[ls(p)].l+1)?node[ls(p)].Max+node[rs(p)].lmax:node[ls(p)].lmax;

		node[p].rmax=node[rs(p)].Max==(node[rs(p)].r-node[rs(p)].l+1)?node[rs(p)].Max+node[ls(p)].rmax:node[rs(p)].rmax;
	}

	inline void build(int p,int l,int r)
	{
		node[p].l=l,node[p].r=r;

		if(node[p].l==node[p].r)
		{
			node[p].lmax=node[p].rmax=node[p].Max=1;

			return;
		}

		int mid=node[p].l+node[p].r>>1;

		Segment_Tree::build(ls(p),l,mid);

		Segment_Tree::build(rs(p),mid+1,r);

		Segment_Tree::pushup(p);
	}

	inline void pushdown(int p)
	{
		if(node[p].tag==0)
		{
			node[ls(p)].tag=node[rs(p)].tag=0;

			node[ls(p)].Max=node[ls(p)].lmax=node[ls(p)].rmax=node[ls(p)].r-node[ls(p)].l+1;

			node[rs(p)].Max=node[rs(p)].lmax=node[rs(p)].rmax=node[rs(p)].r-node[rs(p)].l+1;
		}

		else if(node[p].tag==1)
		{
			node[ls(p)].tag=node[rs(p)].tag=1;

			node[ls(p)].Max=node[ls(p)].lmax=node[ls(p)].rmax=0;

			node[rs(p)].Max=node[rs(p)].lmax=node[rs(p)].rmax=0;
		}

		node[p].tag=-1;
	}

	inline void update(int x,int y,int p,int k)
	{
		if(x<=node[p].l && node[p].r<=y)
		{
			if(k==0)
			{
				node[p].Max=node[p].lmax=node[p].rmax=node[p].r-node[p].l+1;
			}

			else
			{
				node[p].Max=node[p].lmax=node[p].rmax=0;
			}

			node[p].tag=k;

			return;
		}

		Segment_Tree::pushdown(p);

		int mid=node[p].l+node[p].r>>1;

		if(x<=mid)
		{
			Segment_Tree::update(x,y,ls(p),k);
		}

		if(y>mid)
		{
			Segment_Tree::update(x,y,rs(p),k);
		}

		Segment_Tree::pushup(p);
	}

	inline int query(int p,int k)
	{
		if(node[p].l==node[p].r)
		{
			return node[p].l;
		}

		Segment_Tree::pushdown(p);

		int mid=node[p].l+node[p].r>>1;

		if(node[ls(p)].Max>=k)
		{
			return Segment_Tree::query(ls(p),k);
		}

		if(node[ls(p)].rmax+node[rs(p)].lmax>=k)
		{
			return node[ls(p)].r-node[ls(p)].rmax+1;
		}

		else
		{
			return Segment_Tree::query(rs(p),k);
		}
	}

	#undef ls

	#undef rs
}

int main(void)
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif

	n=read(),m=read();

	Segment_Tree::build(1,1,n);

	int ans=0;

	for(register int i=1;i<=m;i++)
	{
		char ch[3];

		scanf("%s",ch);

		int x=read();

		if(ch[0]=='A')
		{
			if(node[1].Max>=x)
			{
				int l=Segment_Tree::query(1,x);

				Segment_Tree::update(l,l+x-1,1,1);
			}

			else
			{
				ans++;
			}
		}

		else	
		{
			int y=read();

			Segment_Tree::update(x,y,1,0);
		}
	}

	printf("%d\n",ans);

	return 0;
}
上一篇:安装和加载R程序包


下一篇:第7章 更灵活的定位内存地址的方法