1. 程式人生 > >P1083 借教室(二分)

P1083 借教室(二分)

這個題的單調性在哪裡呢?

顯然(額),如果你在分配第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;
}