[vijos1782][NOIP2012]借教室

Description

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。我们需要处理接下来\(n\)天的借教室信息,其中第\(i\)天学校有\(r_i\)个教室可供租借。共有\(m\)份订单,每份订单用三个正整数描述,分别为\(d_j,s_j,t_j\),表示某租借者需要从第\(s_j\)天到第\(t_j\)天租借教室(包括第\(s_j\)天和第\(t_j\)天),每天需要租借\(d_j\)个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供\(d_j\)个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第\(s_j\)天到第\(t_j\)天中有至少一天剩余的教室数量不足\(d_j\)个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

HINT

\(1≤n,m≤10^6,0≤r_i,d_j≤10^9,1≤s_j≤t_j≤n\).

Solution

简单粗暴做法

线段树维护区间最大值.

#define N 1000005
#define M 3000005
typedef long long ll;
struct SegTree{
	int m,d;
}lt[M];
int a[N],n,m;
inline void build(int u,int ll,int rr){
	if(ll<rr){
		int lef=u<<1,rig=u<<1|1,mid=ll+rr>>1;
		build(lef,ll,mid);build(rig,mid+1,rr);
		lt[u].m=min(lt[lef].m,lt[rig].m);
	}
	else lt[u].m=a[ll];
}
inline bool add(int u,int ll,int rr,int l,int r,int k){
	if(ll>=l&&rr<=r){
		lt[u].d+=k;lt[u].m-=k;
		return lt[u].m>=0;
	}
	if(ll<rr){
		int lef=u<<1,rig=u<<1|1,mid=ll+rr>>1;
		lt[lef].d+=lt[u].d;lt[lef].m-=lt[u].d;
		lt[rig].d+=lt[u].d;lt[rig].m-=lt[u].d;
		lt[u].d=0;
		if(l<=mid) add(lef,ll,mid,l,r,k);
		if(r>mid) add(rig,mid+1,rr,l,r,k);
		lt[u].m=min(lt[lef].m,lt[rig].m);
		return lt[u].m>=0;
	}
}
inline void Aireen(){
	n=read();m=read();
	for(int i=1;i<=n;++i)
		a[i]=read();
	build(1,1,n);
	for(int i=1,l,r,k;i<=m;++i){
		k=read();l=read();r=read();
		if(!add(1,1,n,l,r,k)){
			printf("-1\n%d\n",i);
			return;
		}
	}
	puts("0");
}

这才是正解

  • 单调性:如果前\(i\)天合法,则前\((i-1)\)天也合法,所以考虑二分.
  • 判断可行性:差分:令\(d_i=a_i-a_{i-1}\),则区间\([l,r]+k\)等价于\(d_l+k,a_{r+1}-k\),先用差分处理操作,最后还原回\(a_i\)判断即可.
#define N 1000005
typedef long long ll;
ll s[N],d[N];
int a[N],l[N],r[N],n,m,lef,rig,mid;
inline bool chk(int x){
	memset(s,0,sizeof(s));
	for(int i=1;i<=x;++i)
		s[l[i]]+=d[i],s[r[i]+1]-=d[i];
	ll now=0ll;
	for(int i=1;i<=n;++i){
		now+=s[i];
		if(now>a[i]) return false;
	}
	return true;
}
inline void Aireen(){
	n=read();m=read();
	for(int i=1;i<=n;++i)
		a[i]=read();
	for(int i=1;i<=m;++i){
		d[i]=1ll*read();l[i]=read();r[i]=read();
	}
	if(chk(n)){
		 puts("0");return;
	}
	lef=1;rig=n;
	while(lef<rig){
		mid=lef+rig>>1;
		if(!chk(mid)) rig=mid;
		else lef=mid+1;
	}
	printf("-1\n%d\n",lef);
}

2017-10-27 13:45:00

posted @ 2021-11-25 14:11  Aireen_Ye  阅读(20)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.