HDU - 4614

比较简单,一发A。

这几个操作很明显线段树维护。

初是区间都是0,代表某个区间花数量和

2 a b 把区间赋值成0,查询区间和。

1 a f 查询  a - n-1 区间无花数量是否小于f  小于的花直接全部插上花,插之前询问最左边无花,最右边无花的瓶子位置即最小值最右边//最左 的坐标。

如果区间无花数量大于f。则二分查找区间无花数量第一个大于等于f的位置。刚好这个区间插满花,右边为pos左边为区间最左边无花位置。

//KX
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int M = 5e4+7;
#define ls o*2
#define rs o*2+1
#define pb push_back
int st[M<<2],tag[M<<2],mn[M<<2];
void pd(int o,int l,int r)
{
	if(tag[o]==-1)return ;
	int m=(l+r)/2;
	mn[ls]=mn[rs]=tag[ls]=tag[rs]=tag[o];
	st[ls]=tag[o]*(m-l+1);
	st[rs]=tag[o]*(r-m);
	tag[o]=-1;
	return ;
}
void bd(int o,int l,int r)
{
	mn[o]=st[o]=0,tag[o]=-1;
	if(l==r)
	{
		return ;
	}
	int m=(l+r)/2;
	bd(ls,l,m);
	bd(rs,m+1,r);
}
void up(int o,int l,int r,int x,int y,int d)//区间覆盖 
{
//	printf("%d   %d   %d   %d\n",l,r,x,y);
	if(x<=l&&r<=y)
	{
		mn[o]=d;
		st[o]=d*(r-l+1);
		tag[o]=d;
		return ;
	}
	pd(o,l,r);
	int m=(l+r)/2;
	if(x<=m)up(ls,l,m,x,y,d);
	if(y>m)up(rs,m+1,r,x,y,d);
	mn[o]=min(mn[ls],mn[rs]);
	st[o]=st[ls]+st[rs];
}
int qu(int o,int l,int r,int x,int y)//区间和
{
	if(x<=l&&r<=y)
	{
		return st[o];
	}
	pd(o,l,r);
	int m=(l+r)/2;
	int ans=0;
	if(x<=m)ans+=qu(ls,l,m,x,y);
	if(y>m)ans+=qu(rs,m+1,r,x,y);
	return ans;
}
bool fl;
int ps;
void qul(int o,int l,int r,int x,int y)//区间最小值  最左边 
{
//	printf("%d  %d  %d    %d %d\n",l,r,mn[o],x,y);
	if(l>y||r<x||fl)return;
	
	if(l==r)
	{
		if(mn[o]==0)
		fl=true,ps=l;
		return;
	}
	pd(o,l,r);
	int m=(l+r)/2;
	if(mn[ls]==0)qul(ls,l,m,x,y);
	if(mn[rs]==0)qul(rs,m+1,r,x,y);
}
void qur(int o,int l,int r,int x,int y)//区间最小值  最右边 
{
	if(l>y||r<x||fl)return;
	if(l==r)
	{
		if(mn[o]==0)
		fl=true,ps=l;
		return;
	}
	pd(o,l,r);
	int m=(l+r)/2;
	if(mn[rs]==0)qur(rs,m+1,r,x,y);
	if(mn[ls]==0)qur(ls,l,m,x,y);
}
int main()
{
	int t,n,q,k,a,b;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&q);
		bd(1,1,n);
		while(q--)
		{
			scanf("%d%d%d",&k,&a,&b);
			a++;
			if(k==1)
			{
				int w=qu(1,1,n,a,n);
				//printf("---  %d\n",w);
				w=n-a+1-w;
				if(w==0)
				{
					puts("Can not put any one.");
					continue;
				}
				fl=false;
				qul(1,1,n,a,n);
				int pl=ps;//a-n最小值最左边的位置 
				if(w<=b)
				{
					fl=false;
					qur(1,1,n,a,n);
					int pr=ps;
					up(1,1,n,a,n,1);
					printf("%d %d\n",pl-1,pr-1);
					continue;
				}
			//	puts("okk");
				int l=pl,r=n,pos;
				while(l<=r)
				{
					int mid=(l+r)/2;
					w=qu(1,1,n,pl,mid);//有花的个数 
					w=mid-pl+1-w;//无花的个数 
					if(w>=b)//第一个大于等于b 
					{
						pos=mid;
						r=mid-1;
					}
					else
						l=mid+1;
				}
			//	puts("zzzz");
				up(1,1,n,pl,pos,1);
				printf("%d %d\n",pl-1,pos-1);
			}
			else
			{
				b++;
				int w=qu(1,1,n,a,b);
				up(1,1,n,a,b,0);
				printf("%d\n",w);
			}
			
		}
		puts("");
	 } 
  	return 0;
}

看了网上别人的解法,写了一个纯二分的。这种线段树处理比较简单

只有区间更新,区间赋值。

二分查找第一个无花数量大于1即左边插花位置,第一个无花数量大于要插的花,即右边插花位置

//KX
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int M = 5e4+7;
#define ls o*2
#define rs o*2+1
#define pb push_back
int st[M<<2],tag[M<<2],mn[M<<2];
void pd(int o,int l,int r)
{
	if(tag[o]==-1)return ;
	int m=(l+r)/2;
	mn[ls]=mn[rs]=tag[ls]=tag[rs]=tag[o];
	st[ls]=tag[o]*(m-l+1);
	st[rs]=tag[o]*(r-m);
	tag[o]=-1;
	return ;
}
void bd(int o,int l,int r)
{
	mn[o]=st[o]=0,tag[o]=-1;
	if(l==r)
	{
		return ;
	}
	int m=(l+r)/2;
	bd(ls,l,m);
	bd(rs,m+1,r);
}
void up(int o,int l,int r,int x,int y,int d)//区间覆盖 
{
//	printf("%d   %d   %d   %d\n",l,r,x,y);
	if(x<=l&&r<=y)
	{
		mn[o]=d;
		st[o]=d*(r-l+1);
		tag[o]=d;
		return ;
	}
	pd(o,l,r);
	int m=(l+r)/2;
	if(x<=m)up(ls,l,m,x,y,d);
	if(y>m)up(rs,m+1,r,x,y,d);
	mn[o]=min(mn[ls],mn[rs]);
	st[o]=st[ls]+st[rs];
}
int qu(int o,int l,int r,int x,int y)//区间和
{
	if(x<=l&&r<=y)
	{
		return st[o];
	}
	pd(o,l,r);
	int m=(l+r)/2;
	int ans=0;
	if(x<=m)ans+=qu(ls,l,m,x,y);
	if(y>m)ans+=qu(rs,m+1,r,x,y);
	return ans;
}
int main()
{
	int t,n,q,k,a,b;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&q);
		bd(1,1,n);
		while(q--)
		{
			scanf("%d%d%d",&k,&a,&b);
			/*for(int i=1;i<=n;i++)
			printf("%d   ",qu(1,1,n,i,i));
			puts("ok");*/
			a++;
			if(k==1)
			{
				int w=qu(1,1,n,a,n);
				//printf("---  %d\n",w);
				w=n-a+1-w;
				if(w==0)
				{
					puts("Can not put any one.");
					continue;
				}
				if(w<b)b=w;
				int l=a,r=n,pl;
				while(l<=r)
				{
					int mid=(l+r)/2;
					w=qu(1,1,n,a,mid);//有花的个数 
					w=mid-a+1-w;//无花的个数 
					if(w>=1)//第一个大于等于1 
					{
						pl=mid;
						r=mid-1;
					}
					else
						l=mid+1;
				}
				
				l=pl,r=n;
				int pos=n;
				while(l<=r)
				{
					int mid=(l+r)/2;
					w=qu(1,1,n,pl,mid);//有花的个数 
					w=mid-pl+1-w;//无花的个数 
					if(w>=b)//第一个大于等于b 
					{
						pos=mid;
						r=mid-1;
					}
					else
						l=mid+1;
				}
			//	puts("zzzz");
				up(1,1,n,pl,pos,1);
				printf("%d %d\n",pl-1,pos-1);
			}
			else
			{
				b++;
				int w=qu(1,1,n,a,b);
				up(1,1,n,a,b,0);
				printf("%d\n",w);
			}
			
		}
		puts("");
	 } 
  	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值