(HDU4614)Vases and Flowers(线段树+二分)

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=4614

先给出中文翻译吧:

爱丽丝是如此受欢迎,以至于她每天都能收到许多鲜花。她有 N 个花瓶,编号从 0 到 N-1。当她收到一些花时,她会试着把它们放在花瓶里,一花一瓶。她随机选择了花瓶 A 并尝试在花瓶中放入一朵花。如果花瓶里没有花,她会在里面放一朵花,否则她跳过这个花瓶。然后她会尝试放入花瓶 A+1、A+2、...、N-1,直到没有花或她尝试过花瓶 N-1。剩下的花将被丢弃。当然,有时她会打扫花瓶。因为花瓶太多,她随机选择清洗编号为A到B(A <= B)的花瓶。清洗过的花瓶中的花将被丢弃。

输入

  第一行包含一个整数 T,表示测试用例的数量。
  对于每个测试用例,第一行包含两个整数 N(1 < N < 50001) 和 M(1 < M < 50001)。N是花瓶的数量,M是Alice的操作。接下来的 M 行中的每一行都包含三个整数。一行的第一个整数是 K(1 或 2)。如果 K 是 1,则后面跟着两个整数 A 和 F。这意味着爱丽丝收到了 F 朵花,并尝试先将一朵花放入花瓶 A 中。如果 K 是 2,则后面跟着两个整数 A 和 B。这意味着主人想清洁从 A 到 B(A <= B)编号的花瓶。

输出

  对于 K 为 1 的每个操作,输出 Alice 放第一朵花和最后一朵花的花瓶的位置,用空格隔开。如果她不能放任何一个,那么输出'不能放任何一个.'。对于 K 为 2 的每个操作,输出丢弃的花数。
  每个测试用例后输出一个空行。

分析:对于操作2,就是简单的区间修改,关键是如何处理操作1,我们可以先找一下从给定编号的花瓶到编号为n-1的花瓶一共还能放几朵花,如果是0,就直接输出 Can not put any one.  如果可放花的数量多于所需放花的数量,那我们需要放几朵花就放几朵花,否则能放几朵花就放几朵花,我们可以通过二分来找放花的起点和终点,具体看代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=3e5+10;
int sum[N],l[N],r[N],lazy[N];
void pushup(int id)
{
	sum[id]=sum[id<<1]+sum[id<<1|1];
}
void pushdown(int id)
{
	if(lazy[id]!=-1)//涉及到区间赋值为0操作,lazy数组初始化为-1 
	{
		sum[id<<1]=(r[id<<1]-l[id<<1]+1)*lazy[id];
		sum[id<<1|1]=(r[id<<1|1]-l[id<<1|1]+1)*lazy[id];
		lazy[id<<1]=lazy[id];
		lazy[id<<1|1]=lazy[id];
		lazy[id]=-1;
	}
}
void build(int id,int L,int R)
{
	//涉及到区间赋值为0操作,lazy数组初始化为-1  
	l[id]=L;r[id]=R;sum[id]=0;lazy[id]=-1;
	if(L==R) return ;
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
	pushup(id);	
} 
int query_interval(int id,int L,int R)
{
	if(l[id]>R||r[id]<L) return 0;
	if(l[id]>=L&&r[id]<=R) return sum[id];
	pushdown(id);
	return query_interval(id<<1,L,R)+query_interval(id<<1|1,L,R);
}
void update_interval(int id,int L,int R,int val)
{
	if(l[id]>R||r[id]<L) return ;
	if(l[id]>=L&&r[id]<=R)
	{
		sum[id]=val*(r[id]-l[id]+1);
		lazy[id]=val;
		return ;
	}
	pushdown(id);
	update_interval(id<<1,L,R,val);
	update_interval(id<<1|1,L,R,val);
	pushup(id);
}
int main()
{
	int T,n,m;
	cin>>T;
	while(T--)
	{
		scanf("%d%d",&n,&m);
		build(1,1,n);
		int x,y,op;
		while(m--)
		{
			scanf("%d%d%d",&op,&x,&y);
			if(op==1)
			{
				x++;//将区间变为1~n 
				int t=min(y,n-x+1-query_interval(1,x,n));
				if(!t)
				{
					printf("Can not put any one.\n");
					continue;
				}
				//二分找起点 
				int ll=x,rr=n;
				while(ll<rr)
				{
					int mid=ll+rr>>1;
					if(mid-x+1-query_interval(1,x,mid)>=1) rr=mid;
					else ll=mid+1;
				}
				printf("%d ",ll-1);
				//二分找终点 
				ll=x,rr=n;
				while(ll<rr)
				{
					int mid=ll+rr>>1;
					if(mid-x+1-query_interval(1,x,mid)>=t) rr=mid;
					else ll=mid+1;
				}
				printf("%d\n",ll-1);
				//别忘了查询到起点和终点后将此区间内都装满花 
				update_interval(1,x,ll,1);
			}
			else
				{
					x++;y++;//将区间变为1~n 
					printf("%d\n",query_interval(1,x,y));
					update_interval(1,x,y,0);
				}
		}
		printf("\n"); 
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值