Vases and Flowers

这道题目很多人用二分+线段树来做,但是(可能我太水了吧)想不到怎么用二分,只好照着常规思路来做

题意:

两种操作:1.从a号花瓶开始插入f朵花,每个花瓶只能有一朵花,且有花的花的花瓶不能被放入 2.清除一个区间内花瓶中的花 注意花瓶的编号是0~n-1

解题思路:

这就是两种不同的区间更新,先设置一个结构体

struct list
{
	int num;
	int weight;
}tree[4*maxn];
num表示在left和right范围内花瓶中的花的数量,weight权值1表示这个范围内花瓶中都有花,-1表示花瓶中都没有花,0表示有的花瓶中有花有的没有

四个线段树函数:

void build(int i,int left,int right);
void update_add(int i,int left,int right);
void update_del(int i,int ql,int qr,int left,int right);
void pushup(int i,int left,int right);
void pushdown(int i,int left,int right);
build函数将tree数组中的num赋值为0,weight赋值为-1,表示起初花瓶都是空的,update_add函数计算添加花的情况,update_del函数计算去除花的情况,pushup函数更新父节点,pushdown函数更新子节点

			if(op==1)
			{
				scanf("%d%d",&L,&ans);
				L++;
				R=L+ans-1;l=-1,r=-1;		
				update_add(1,1,n);
				if(r==-1)
					printf("Can not put any one.\n");
				else
					printf("%d %d\n",l-1,r-1);
			}
插花的操作,因为花瓶的编号可能为零所以将更新的左边界+1,而右边界是可以改变,随着插花向右增大,最大为n

			else if(op==2)
			{
				int a,b;
				scanf("%d%d",&a,&b);
				ans=0;
				update_del(1,a+1,b+1,1,n);
				printf("%d\n",ans);
			}
丢花的操作就是一个区间更新,注意边界都要+1

void update_add(int i,int left,int right)
{
	if(ans<=0)
		return ;
	if(r==n)
		return ;
	if(L<=left&&R>=right&&tree[i].weight)
	{
		if(!tree[i].num)
		{
			if(l<0)
				l=left;
			if(right>r)
				r=right;
			ans-=right-left+1;
			tree[i].num=right-left+1;
			tree[i].weight=1;
		}
		else
		{
			R+=(right-left+1);
			if(R>n)
				R=n;
		}
		return ;
	}
	if(tree[i].weight)
		pushdown(i,left,right);
	int mid=(left+right)>>1;
	if(L<=mid)
		update_add(i<<1,left,mid);
	if(R>mid)
		update_add(i<<1|1,mid+1,right);
	pushup(i,left,right);
}
最关键的是第三个if语句中的,如果逻辑不好就有超时的风险,简单说一下,当这个区间在L和R内且全为1或0时,执行内部的语句,如果全为0,就把花插上更新起点、终点,如果全为1,就就扩大右边界(最大为n)

Alice有N个花瓶(标号为0~ N-1)。当她收到一些花时,她会随机的选择一个瓶子A,从它开始遍历A,A+1, A+2, ..., N-1号瓶子,遇到空瓶子就放一朵花进去,直到花朵放完或没有瓶子,剩下的花将被丢弃。有时,她也会清理标号从A到B的花瓶(A <= B).花瓶里的花会被丢弃。
Input

  第一行一个整数T,表示数据组数。
  每组数据,第一行一个整数N(1 < N < 50001) and M(1 < M < 50001). N 是花瓶个数,  M 是Alice的操作次数. 接下来M行 行3个 整数. 第一个整数K(1 or 2). 如果K=1, 后面跟两个整数 A 和  F . 表示Alice 得到了F 朵花并且把它们放入从A 的花瓶里. 如果K= 2, 后跟两个整数 A 和  B. 表示Alice  清理的花瓶标号范围(A <= B).

Output

  对于每个K=1的操作,输出第一朵和最后一朵花放置的花瓶标号。如果没有任何放花的位置,输出'Can not put any one.'.对于K=2的操作,输出丢弃花的个数.
   每组数据后输出一个空行.

Sample Input
2
10 5
1 3 5
2 4 5
1 1 8
2 3 6
1 8 8
10 6
1 2 5
2 3 4
1 0 8
2 2 5
1 4 4
1 2 3
Sample Output
3 7
2
1 9
4
Can not put any one.

2 6
2
0 9
4
4 5
2 3


#include<stdio.h>
#include<string.h>
#define maxn 50005
struct list
{
	int num;
	int weight;
}tree[4*maxn];
int ans,l,r,n,L,R;
void build(int i,int left,int right);
void update_add(int i,int left,int right);
void update_del(int i,int ql,int qr,int left,int right);
void pushup(int i,int left,int right);
void pushdown(int i,int left,int right);
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int m;
		scanf("%d%d",&n,&m);
		build(1,1,n);
		for(int i=0;i<m;i++)
		{
			int op;
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d%d",&L,&ans);
				L++;
				R=L+ans-1;l=-1,r=-1;		
				update_add(1,1,n);
				if(r==-1)
					printf("Can not put any one.\n");
				else
					printf("%d %d\n",l-1,r-1);
			}
			else if(op==2)
			{
				int a,b;
				scanf("%d%d",&a,&b);
				ans=0;
				update_del(1,a+1,b+1,1,n);
				printf("%d\n",ans);
			}
		}
		putchar('\n');
	}
	return 0;
}
void build(int i,int left,int right)
{
	tree[i].num=0;
	tree[i].weight=-1;
	if(left==right)
		return ;
	int mid=(left+right)>>1;
	build(i<<1,left,mid);
	build(i<<1|1,mid+1,right);
}
void update_add(int i,int left,int right)
{
	if(ans<=0)
		return ;
	if(r==n)
		return ;
	if(L<=left&&R>=right&&tree[i].weight)
	{
		if(!tree[i].num)
		{
			if(l<0)
				l=left;
			if(right>r)
				r=right;
			ans-=right-left+1;
			tree[i].num=right-left+1;
			tree[i].weight=1;
		}
		else
		{
			R+=(right-left+1);
			if(R>n)
				R=n;
		}
		return ;
	}
	if(tree[i].weight)
		pushdown(i,left,right);
	int mid=(left+right)>>1;
	if(L<=mid)
		update_add(i<<1,left,mid);
	if(R>mid)
		update_add(i<<1|1,mid+1,right);
	pushup(i,left,right);
}
void update_del(int i,int ql,int qr,int left,int right)
{
	if(ql<=left&&qr>=right)
	{
		ans+=tree[i].num;
		tree[i].num=0;
		tree[i].weight=-1;
		return ;
	}
	if(tree[i].weight)
		pushdown(i,left,right);
	int mid=(left+right)>>1;
	if(ql<=mid)
		update_del(i<<1,ql,qr,left,mid);
	if(qr>mid)
		update_del(i<<1|1,ql,qr,mid+1,right);
	pushup(i,left,right);
}
void pushup(int i,int left,int right)
{
	tree[i].num=tree[i<<1].num+tree[i<<1|1].num;
	if(tree[i<<1].num+tree[i<<1|1].num==right-left+1)
		tree[i].weight=1;
	else if(tree[i<<1].num+tree[i<<1|1].num==0)
		tree[i].weight=-1;	
}
void pushdown(int i,int left,int right)
{
	if(tree[i].weight==1)
	{
		tree[i<<1].weight=1;
		tree[i<<1|1].weight=1;
		int mid=(left+right)>>1;
		tree[i<<1].num=mid-left+1;
		tree[i<<1|1].num=right-mid;
		tree[i].weight=0;
	}
	else if(tree[i].weight==-1)
	{
		tree[i<<1].weight=-1;
		tree[i<<1|1].weight=-1;
		tree[i<<1].num=0;
		tree[i<<1|1].num=0;
		tree[i].weight=0;
	}
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值