1. 程式人生 > >P1083 借教室(線段樹)

P1083 借教室(線段樹)

其實我第一次做借教室是用線段樹做的2333。

顯然(額),如果你在分配第i個訂單時就gg了,那麼往後的訂單就都沒法分配了。

那麼gg的條件是什麼?

設陣列a[i]表示分配完第j個訂單後第i天還有多少個教室供後來的第j個後的訂單分配,那麼,當a陣列中出現a[i]< 0的情況時,表示第i天的教室已經被多個訂單分配且無法滿足所有的前j個訂單。這時就gg了。

所以,我們開一棵線段樹維護a陣列的最小值,將訂單d{}_{j},s{}_{j},t{}_{j}看作為第j個修改操作,將[s{}_{j},t{}_{j}]的所有值減去d{}_{j}。當修改完後min(a{}_{i})小於0,那麼當前的訂單就gg了,直接輸出-1和j即可。若在進行完所有操作後min(a{}_{i})始終\geq 0,則說明所有訂單之間互不衝突,輸出0即可。

程式碼:

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

const int MAXN=1000020;
int n,q,a[MAXN],d[MAXN],s[MAXN],t[MAXN];
int l[MAXN<<2],r[MAXN<<2],minn[MAXN<<2],tag[MAXN<<2];

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;
}

void pushup(int p)
{
	minn[p]=min(minn[p <<1],minn[p <<1|1]);
}

void pushdown(int p)
{
	minn[p <<1]-=tag[p],tag[p <<1]+=tag[p];
	minn[p <<1|1]-=tag[p],tag[p <<1|1]+=tag[p];
	tag[p]=0;
}

void build(int p,int lft,int rit)
{
	l[p]=lft,r[p]=rit;
	if(l[p]==r[p])
	{
		minn[p]=a[lft];
		return;
	}
	int mid=(lft+rit)>>1;
	build(p <<1,lft,mid);
	build(p <<1|1,mid+1,rit);
	pushup(p);
}

void update(int p,int lft,int rit,int k)
{
	if(lft<=l[p]&&r[p]<=rit)
	{
		minn[p]-=k;tag[p]+=k;
		return;
	}
	pushdown(p);
	if(lft<=r[p <<1])  update(p <<1,lft,rit,k);
	if(l[p <<1|1]<=rit)  update(p <<1|1,lft,rit,k);
	pushup(p);
}

int main()
{
	n=read(),q=read();
	for(ri i=1;i<=n;i++)	a[i]=read();
	build(1,1,n);
	for(ri i=1;i<=q;i++)	d[i]=read(),s[i]=read(),t[i]=read();
	for(ri i=1;i<=q;i++)
	{
		update(1,s[i],t[i],d[i]);
		if(minn[1]<0)  { cout<<"-1"<<'\n'<<i; return 0; }
	}
	cout<<"0";
	return 0;
}