P12360 题解
模拟赛 T1。
每次要对没确定的位作最坏的打算,那么当新的一位确定后新的最坏的打算一定不会比之前的更坏,也就是说获得的分数可能会更优。于是答案单调,考虑二分答案。
设当前 check 的值是
考虑
而如果有好几个「强者」能打败当前对方守将拿下当前场,显然选技能值最低的「强者」拿下即可。剩余几位技能值较高的留在后面可能会发挥更大的用处。
如果当前没有「强者」,也就是没有
显然用 set 可以直接维护我们当前可用人的技能值的集合。
- 使用
st.lower_bound(x)寻找最小的能打败守将x 的「强者」。 - 如果没有找到「强者」,那就排
st.begin()。 - 每次从
set中找到我们当前出战的人员y 后用st.erase(y)将y 从可用人集合中删除即可。 - 以上操作都是
\mathcal{O}(\log n) 的,非常快速。
每次对战记录上场的我方队员是否能打过守将。如果能打过就加上当前赛场的贡献。check 的返回值即判断总贡献是否能
复杂度分析:set 带一只
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,sum,a[N],b[N];
struct node{int id,val;}w[N];
bool cmp(node X,node Y){return X.val>Y.val;}
bool check(int mid){
set<int>st1,st2;
for(int i=1;i<=n;i++){
st2.insert(b[i]);
if(i>mid)st1.insert(-a[i]);
}
int ans=0;
for(int i=1;i<=n;i++){
int tmp1,tmp2;
if(w[i].id<=mid)tmp2=a[w[i].id];
else{
auto it=st1.begin();
tmp2=-(*it),st1.erase(it);
}
auto tt=st2.lower_bound(tmp2);
if(tt!=st2.end())tmp1=*tt,st2.erase(tt);
else tmp1=*st2.begin(),st2.erase(st2.begin());
if(tmp1>tmp2)ans+=w[i].val;
}
return ans>=sum/2+1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>w[i].val,w[i].id=i,sum+=w[i].val;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
sort(w+1,w+n+1,cmp);
int lt=-1,rt=n+1;
while(lt+1<rt){
int mid=lt+rt>>1;
if(check(mid))rt=mid;
else lt=mid;
}
cout<<(rt==n+1?-1:rt);
return 0;
}