Description
奶牛们开了一家餐馆。该餐馆里有N(1 <= N <= 500,000)个排成一列的座位(编号1到N),编号越小的座位越靠近窗户。早晨开业时,座位都是空的。
今天餐馆里发生了M(1 <= M <= 300,000)个事件,这些事件总共分两类:
1.一伙人一起来就餐,该伙人共P(1 <= p <= N)个人,这伙人想坐在一段连续的位置上就餐,如果能坐下,他们希望座位尽量靠近窗户。如果无法坐下,他们会马上离开。
2.坐在a号到b号(1 <= a <= b <= N) 这连续一段座位的客人用餐完毕后离开了。
请帮贝西计算今天有多少伙人因为没有满足他们要求的座位而离开了。
Input
第一行,两个空格间隔的整数N和M
接下来M行,按时间先后描述了今天发生的事件:
字母A和一个整数P表示一伙P个人到达的餐馆。
字母L和两个整数a,b表示a到b这一段位置的客人离开了。
Output
一个整数,表示所求的结果
Sample Input
10 4
A 6
L 2 4
A 5
A 2
Sample Output
1
【分析】
这是一类线段树的题,在刘汝佳教练的书上也有介绍。区别于普通的线段树,我们新增加三个域lv,rv,mv。分别表示当前线段树节点对应的区间最长的连续的靠左的一段0的长度,连续的靠右的一段0的长度,连续的一段0的长度。其中,0表示位置为空,1表示位置被占。
如图所示:
图中所给的区间lv=2,rv=1,mv=5。我们取Mid后发现lv,rv,mv是可以用左右儿子节点的信息推导的,具体式子代码中有体现。
对于每个A询问,我们先在线段树中查找尽量靠左(靠窗户)的长度大于等于人数的空白区间。然后判断是否可行后,插入即可。
【代码】
/**************************
ID:Ciocio
LANG:C++
DATE:2013-12-20
TASK:Sit
**************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define MAXN 500005
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
#define cd (Right+1-Left)
#define lcd (Mid+1-Left)
#define rcd (Right-Mid)
struct node{
int lv,rv,mv,lazy;
void push(int x,int y,int z,int c)
{lv=x;rv=y;mv=z;lazy=c;}
}Tree[MAXN<<2];
int N,M,ans;
void _read(int &x)
{
char tt=getchar();
while(tt<'0'||'9'<tt) tt=getchar();
for(x=0;'0'<=tt&&tt<='9';x=(x<<3)+(x<<1)+tt-'0',tt=getchar());
}
void _read(char &x){for(x=getchar();x!='A'&&x!='L';x=getchar());}
int max(int a,int b,int c){return max(a,max(b,c));}
void _build(int id,int Left,int Right)
{
int Mid=(Left+Right)>>1;
Tree[id].push(cd,cd,cd,-1);
if(Left==Right) return;
_build(lson(id),Left,Mid);
_build(rson(id),Mid+1,Right);
}
void _init()
{
_read(N);_read(M);
_build(1,1,N);
}
void _putdown(int id,int Left,int Right) //lazy的下放
{
if(Tree[id].lazy==-1||Left==Right)
{Tree[id].lazy=-1;return;}
int val=Tree[id].lazy,Mid=(Left+Right)>>1;
if(val==1)
{
Tree[lson(id)].push(0,0,0,1);
Tree[rson(id)].push(0,0,0,1);
}
else
{
Tree[lson(id)].push(lcd,lcd,lcd,0);
Tree[rson(id)].push(rcd,rcd,rcd,0);
}
Tree[id].lazy=-1;
}
int _find(int id,int Left,int Right,int len)
{
if(Tree[id].mv<len) return -1;
_putdown(id,Left,Right);
int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
if(Tree[ls].mv>=len) return _find(ls,Left,Mid,len); //如果左儿子区间可以满足,去左儿子查找
if(Tree[ls].rv+Tree[rs].lv>=len) return Mid+1-Tree[ls].rv; //如果左右儿子拼凑后满足,返回答案
if(Tree[rs].mv>=len) return _find(rs,Mid+1,Right,len); //同上
return -1;
}
void _change(int id,int Left,int Right,int l,int r,int val)
{
if(l<=Left&&Right<=r)
{
if(val==1) Tree[id].push(0,0,0,1);
else Tree[id].push(cd,cd,cd,0);
return;
}
int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
_putdown(id,Left,Right);
if(!(r<Left||Mid<l)) _change(ls,Left,Mid,l,r,val);
if(!(r<Mid+1||Right<l)) _change(rs,Mid+1,Right,l,r,val);
Tree[id].lv=Tree[ls].lv; //回溯时更新lv,rv,mv域
if(Tree[ls].lv==lcd) Tree[id].lv+=Tree[rs].lv;
Tree[id].rv=Tree[rs].rv;
if(Tree[rs].rv==rcd) Tree[id].rv+=Tree[ls].rv;
Tree[id].mv=max(Tree[ls].mv,Tree[rs].mv,Tree[ls].rv+Tree[rs].lv);
}
void _solve()
{
int a,b,c;char p;
while(M--)
{
_read(p);
if(p=='A')
{
_read(c);
int loc=_find(1,1,N,c);
if(loc==-1) ans++;
else _change(1,1,N,loc,loc+c-1,1);
}
else
{
_read(a);_read(b);
_change(1,1,N,a,b,0);
}
}
cout<<ans<<endl;
}
int main()
{
_init();
_solve();
return 0;
}