[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