poj3667(线段树区间操作)

poj3667
题意

给出n,m(1<=N,M<=50000),表示一个有N个空位的数列,对其进行M次操作

给出两种操作:

1 Di:表示将数列中最左端的连续Di个空位填满,输出被填满区间左端点的位置,若无法做到则不填并输出0

2 Xi Di:将区间[Xi,Xi+Di-1]清空

题目分析

建一棵线段树,每个节点维护该区间内最大左/右连续空位和最大连续空位和标记f,f==1表示该区间被填满,f==-1表示该区间被清空。利用标记f对线段树进行区间操作。

代码(AC)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=55000;
int n,m;
struct Node{
	int l,r;
	int ln,rn,n;
	int flag;
	Node *lt,*rt;
	Node(int l1,int r1){
		l=l1; r=r1;
		ln=rn=n=flag=0;
		lt=rt=NULL;
	}
};
Node *root;

inline void pushdown(Node *p){
	if (p->flag==1) p->ln=p->rn=p->n=0;
	if (p->flag==-1) p->ln=p->rn=p->n=p->r-p->l+1;
	if (p->lt && p->flag!=0) p->lt->flag=p->rt->flag=p->flag;
	p->flag=0;
}
inline void update(Node *p){
	if (!p->lt){
		if (p->flag==1) p->ln=p->rn=p->n=0;
		if (p->flag==-1 || p->flag==0) p->ln=p->rn=p->n=1;
		return;		
	} 
	pushdown(p->lt); pushdown(p->rt);
	if (p->lt->n==p->lt->r-p->lt->l+1) p->ln=p->lt->n+p->rt->ln;
	else p->ln=p->lt->ln;
	if (p->rt->n==p->rt->r-p->rt->l+1) p->rn=p->rt->n+p->lt->rn;
	else p->rn=p->rt->rn;
	p->n=max(p->lt->rn+p->rt->ln,max(p->lt->n,p->rt->n)); 
}
void build(Node *&p,int l,int r){
	p=new Node(l,r);
	if (l<r){
		int mid=(l+r)>>1;
		build(p->lt,l,mid);
		build(p->rt,mid+1,r);
	}
	update(p);
}
void print(Node *p){
	if (p->lt) print(p->lt);
	printf("%d (%d,%d) %d %d %d %d %d %d\n",p,p->l,p->r,p->ln,p->rn,p->n,p->flag,p->lt,p->rt);
	if (p->rt) print(p->rt);
}
int find(Node *p,int dis){
	pushdown(p);
	if (p->n<dis) return 0;
	if (p->ln>=dis) return p->l;
	if (p->lt && p->lt->n>=dis) return find(p->lt,dis); 
	if (p->lt->rn+p->rt->ln>=dis) return p->lt->r-p->lt->rn+1;
	if (p->rt && p->rt->n>=dis) return find(p->rt,dis);
	return 0;
}
void ins(Node *p,int u,int v){
	pushdown(p);
	if (u<=p->l && p->r<=v){
		p->flag=1;
		p->ln=p->rn=p->n=0;
		return;
	}
	int mid=(p->l+p->r)>>1;
	if (u<=mid) ins(p->lt,u,v);
	if (mid+1<=v) ins(p->rt,u,v);
	update(p);
}
void del(Node *p,int u,int v){
	pushdown(p);
	if (u<=p->l && p->r<=v){
		p->flag=-1;
		p->ln=p->rn=p->n=p->r-p->l+1;
		return;
	}
	int mid=(p->l+p->r)>>1;
	if (u<=mid) del(p->lt,u,v);
	if (mid+1<=v) del(p->rt,u,v);
	update(p);
} 

int main(){
	//freopen("hotel.12.in","r",stdin);
	//freopen("hotel.out","w",stdout); 
	scanf("%d%d",&n,&m);
	build(root,1,n);
	//print(root);
	int type,d,x;
	for (int i=1; i<=m; i++){
		scanf("%d",&type);
		if (type==1){
			scanf("%d",&d);
			int answer=find(root,d);
			printf("%d\n",answer);
			if (answer) ins(root,answer,answer+d-1);
		}
		if (type==2){
			scanf("%d%d",&x,&d); 
			del(root,x,x+d-1);
		}
		//print(root);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值