POJ - 3667 线段树区间合并

题:

点击打开链接

题意:就是问1.你有没有一段长为m的连续区间,如果有的话就输出区间的最小的左端点,2把区间a~a+b的值制空。

思路:就是区间合并的裸题,我们需要存的是 区间左端点向右最多能延伸的区间长度,和区间右端点向左能够移动的区间长度,和区间最长连续的长度,之后我们每次查询,如果左子树长度比我们想要的长度长,那么久继续查询左子树,如果比他小,那我们就查询,左子树右端点可以延伸的长度和右子树左端点可以延伸的长度之和,如果还是不够那就查右子树。

上代码把:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mes(a) memset(a,0,sizeof(a));
const int maxn = 50000+10;
int lsum[maxn<<2] , rsum[maxn<<2] ,msum[maxn<<2],lazy[maxn<<2];
using namespace std;

void pushup(int rt,int m)
{
	rsum[rt] = rsum[rt<<1|1];// 
	lsum[rt] = lsum[rt<<1];
	if(lsum[rt] == m-(m>>1))//如果左子树左端点满了,就加上右子树上的值 。 
	{
		lsum[rt] += lsum[rt<<1|1];
	}
	if(rsum[rt] == (m>>1))//同理 ,右子树右端点满了,就加上左子树上的值。 
	{
		rsum[rt] += rsum[rt<<1];
	}
	msum[rt] = max(msum[rt<<1] , max(msum[rt<<1|1],rsum[rt<<1]+lsum[rt<<1|1]));//那么区间的最大值就是 左子树的最大值,和右子树的最大值和
																				//两个区间 相加。 
}

void pushdown(int rt , int m)
{
	if(lazy[rt] != -1)
	{
		//puts("-------------");
		lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];//把延迟标记下放 
		rsum[rt<<1] = lsum[rt<<1] = msum[rt<<1] = lazy[rt] ? 0 :m-(m>>1);//-1 表示没有延迟标记 ,0表示赋值,1表示制空 
		rsum[rt<<1|1] = lsum[rt<<1|1] = msum[rt<<1|1] = lazy[rt] ? 0:(m>>1);
		lazy[rt] = -1; 
	}
}

void build(int l , int r ,int rt)
{
	rsum[rt] = lsum[rt] = msum[rt] = r-l+1;
	if(l == r)
	{
		return ;
	}
	int m = (l+r)>>1;
	build(lson);
	build(rson);
}

void update(int L,int R,int l,int r,int rt,int v)
{
//	printf("L = %d  R = %d\n",L,R);
	if(l>=L&&R>=r)
	{
		lazy[rt] = v;
		rsum[rt] = lsum[rt] = msum[rt] = v?0:(r-l+1);
		return ;
	}
	pushdown(rt,r-l+1);
	int m = (l+r)>>1;
	if(m>=L)
	update(L,R,lson,v);
	if(R>m)
	update(L,R,rson,v);
	pushup(rt,r-l+1);
}

int query(int w,int l,int r,int rt)
{
	if(l == r)
	{
		return l;
	}
	pushdown(rt , r-l+1);
	int m = (r+l)>>1;
	if(msum[rt<<1]>=w)
	{
		return query(w,lson);
	}
	else if(rsum[rt<<1]+lsum[rt<<1|1] >= w) return m-rsum[rt<<1]+1;
	return query(w,rson);
}
int main()
{
	int n ,m ;
	while(scanf("%d%d",&n,&m) !=EOF)
	{
		int op,a,b;
		memset(lazy , -1 , sizeof(lazy));
		mes(msum);mes(rsum);mes(lsum);
		build(1,n,1);
		for(int i = 0 ; i < m ;i++)
		{
			scanf("%d",&op);
			if(op == 1)
			{
				scanf("%d",&a);
				if(msum[1]<a)
				{
					puts("0");
					continue;
				}
				int q = query(a,1,n,1);
				printf("%d\n",q);
				update(q,q+a-1,1,n,1,1);
			}
			else 
			{
				scanf("%d%d",&a,&b);
			//	printf(" MAX1 = %d\n",msum[1]);
				update(a,a+b-1,1,n,1,0);
			//	printf(" MAX2 = %d\n",msum[1]);
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值