P1083 借教室(二分)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/guapi2333/article/details/83386562

这个题的单调性在哪里呢?

显然(额),如果你在分配第i个订单时就gg了,那么往后的订单就都没法分配了。

所以,我们二分一下到底是哪个订单时gg了:

假设我们check 分配到第mid个订单,设数组a[i]表示分配完第mid个订单后第i天还有多少个教室供后来的第mid后的订单分配,那么,当a数组中出现a[i]< 0的情况时,表示第i天的教室已经被多个订单分配且无法满足所有的前mid个订单。那二分流程就出来了:

1.选取l=0,r=q+1.

2.检查分配到第mid个订单时是否gg,若gg了,则有可能在分配第mid个订单前的订单时就已经gg了,令r=mid.若没有gg,则令l=mid

最后注意:若出现mid=0/q+1的情况时,check函数直接返回合法即可。

最后说一说如何实现check函数的O(n)判断:

由于我只需要知道分配完第mid后的a数组的值,但修改却是mid次的。

多次修改,单次查询:差分数组!!!

上代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define ri register int
using namespace std;

const int MAXN=1000020;
int n,q,a[MAXN],d[MAXN],s[MAXN],t[MAXN],l,r,mid,cha[MAXN],sum[MAXN];

bool check(int lst)
{
	memset(cha,0,sizeof(cha));
	memset(sum,0,sizeof(sum));
	if(lst==0||lst==n+1)	return 1;
	for(ri i=1;i<=lst;i++)	 
		cha[s[i]]-=d[i],cha[t[i]+1]+=d[i];//O(1)修改
	//sum[i]:分配完mid个订单后第i天还剩多少个空余教室 
	//cha[i]:sum[i]-sum[i-1]
	for(ri i=1;i<=n;i++)  sum[i]=sum[i-1]+cha[i];
	for(ri i=1;i<=n;i++)
	{
		sum[i]+=a[i];
		if(sum[i]<0)	return 0;
	}
	return 1;
}

inline int read()
{
	int x=0;
	char ch=getchar();
	while(ch<'0'||'9'<ch)	ch=getchar();
	while('0'<=ch&&ch<='9')
	{
		x=(x <<3)+(x <<1)+(ch-'0');
		ch=getchar();
	}
	return x;
}

int main()
{
	n=read(),q=read();
	for(ri i=1;i<=n;i++)	a[i]=read();
	for(ri i=1;i<=q;i++)	d[i]=read(),s[i]=read(),t[i]=read();
	l=0,r=q+1;
	while(l+1<r)
	{
		mid=(l+r)>>1;
		if(check(mid))	l=mid;
		else	r=mid;
	}
	for(ri i=l;i<=r;i++)
		if(!check(i))	{ cout<<"-1"<<'\n'<<i; return 0; }
	//注意第l和r个订单都有可能gg,所以按分配顺序从小往大判断 
	cout<<"0";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/guapi2333/article/details/83386562