题解:CF1866G Grouped Carriages

· · 题解

思路

二分:

我们能够发现答案具有单调性。

也就是说如果答案 Z=x 能够通过移动满足,则 Z=x+1 也可以通过移动满足。这是显然的。

既然答案具有单调性,我们就可以二分答案。

贪心:

那么现在的问题就变成了如何判断对于一个 Z=x 是否是合法的。

我们考虑对每个车厢进行填充(填充越均匀越好)。

对于第 $i$ 个车厢,若第 $j$ 组乘客可以进入,则需要有 $l_j \le i \le r_j$。 在这些乘客中,贪心的想,我们应该选择 $r_j$ 较小的几组乘客,而且不需要考虑 $l$。 这时为什么呢,我们可以画个图考虑一下。 ![](https://cdn.luogu.com.cn/upload/image_hosting/6to6ebvo.png) $a,b$ 是两个车厢,$b>a$。一共有四组乘客 $c_1,c_2,c_3,c_4$,都满足能够移动到 $a$ 车厢。 首先不难发现,有 $l_1,l_2,l_3,l_4<a<b$,这是因为 $l_1,l_2,l_3,l_4<a$,$a<b$。所以在选择那几组乘客移动到 $a$ 时不需要考虑 $l$,因为 $l$ 如果对 $a$ 成立,对 $b$ 也成立。 对于 $r$,我们要选择红色的两个区间,而不是蓝色的区间。因为区间 $2,4$ 无法到达 $b$,如果不选,他们就会堆在 $a$ 上。换句话说,可以理解为他们的时间比较赶,需要早一点分配,不然就分配不上了。 --- ### 实现: 大体的贪心其实很好想,我们考虑如何实现,具体是 $check$ 函数怎么选。 首先我们要对乘客按组以 $l$ 为关键字排个序,这样就不需要多花心思处理左端点了。 $r$ 我们每次要选最小的,那就搞一个优先队列,把符合 $l_j \le i$ 的点扔到优先队列中。 然后开始填充车厢。 那么如何判断 $r$ 是否合法呢。从分配的角度想,如果在分配完车厢 $i$ 时,最后还剩下了 $r_j \le i$,也就是说存在一组乘客只能被放到前 $i$ 个车厢中,但是这几个车厢已经被填满了,不可能再塞下一组乘客。那么这个 $Z=x$ 就不合法。 --- ## 代码 :::info[代码] ```cpp #include<bits/stdc++.h> #define int long long using namespace std; const int N=2e5; int n; int maxx; struct node { int l,r,a; bool operator <(const node &x)const{return r>x.r;} }carriage[N+5]; bool cmp(node x,node y){return x.l<y.l;} bool check(int k) { priority_queue<node>que; int left=1; for(int i=1;i<=n;i++) { while(left<=n && carriage[left].l<=i) { que.push(carriage[left]); left++; } int need=k; while(need && que.size()) { node x=que.top(); que.pop(); if(need>x.a) need-=x.a; else { x.a-=need; need=0; que.push(x); } } while(que.size() && !que.top().a) que.pop(); if(que.size() && que.top().r<=i) return false; //判断不合法 } if(que.size()) return false; //没分配完也是不合法 return true; } signed main() { cin>>n; for(int i=1;i<=n;i++) cin>>carriage[i].a; for(int i=1;i<=n;i++) { maxx=max(maxx,carriage[i].a); int d; cin>>d; carriage[i].l=max(i-d,(int)1),carriage[i].r=min(i+d,n); } sort(carriage+1,carriage+n+1,cmp); int l=-1,r=maxx+1; while(l+1<r) //二分答案 { int mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } cout<<r<<"\n"; return 0; } ``` :::