题解-2025暑假集训模拟赛4-实战教学
题目描述
给定两个长度为 \(2n\) 的序列 \(a,b\),你需要将 \(1\) 到 \(2n\) 两两配对,若一对为 \(x_i,y_i\) 则贡献为 \(max(a_{x_i},a_{y_i})+max(b_{x_i},b_{y_i})\),现在要求贡献最大值最小。
\(n\le 5\times 10^4,1\le a_i,b_i\le 10^9\)
题目解析
想到二分贡献最大值 \(mx\) 然后检测嘛。这样就需要在已知限制的情况下求最大配对数。
如果钦定每一对里 \(a_{x_i}>a_{y_i}\),枚举每一个 \(i\) 作为 \(x_i\),它可匹配的是 \(a_j<a_i\) 且 \(b_j+a_i\le mx\) 的 \(j\)。
所以如果 \(a_i\) 从大到小枚举,就相当于能选择的 \(i\) 对应的 \(b_i\) 越来越大。这相当于一个可选的 \(b_j\) 的集合随着数轴变大,数轴上有若干点 \(a_i\) 会配对集合里的一个点 \(a_j\)。
由于 \(a_i+b_i\le mx\),事实上 \(a_i\) 自己也在集合中,也会被消耗。那么事实上是操作是“每次减一操作附带提前进行进行减一操作,并且那次不再附带”。
注意到我们判断不合法当且仅当可选的集合大小为 \(0\),所以说只维护集合大小即可。
那么相当于有一个数要保证非负,数轴上有若干固定的加操作(随着 \(a\) 的减小)、减操作。每次减法操作必须同时进行后面的一次减法操作。
我们当然希望减法操作越后面进行越好,所以我们直接选可选的 \(j\) 里在数轴最前面的,也就是 \(a_j\) 的最大值即可。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct jgt{int x,y,id;}a[100005],b[100005];
bool cmp1(jgt x,jgt y){return x.x>y.x;}
bool cmp2(jgt x,jgt y){return x.y<y.y;}
int tag[100005],n;
bool check(int mx)
{
for(int i=1;i<=n;i++)
tag[i]=0;int now=1;
for(int i=1;i<=n;i++)
if(a[i].x+a[i].y>mx) return 0;
priority_queue<pair<int,int> >q;
for(int i=1;i<=n;i++)
if(!tag[a[i].id])
{
tag[a[i].id]=1;
while(now<=n&&b[now].y+a[i].x<=mx)
q.push(make_pair(b[now].x,b[now].id)),now++;
while(!q.empty()&&tag[q.top().second]) q.pop();
if(q.empty()) return 0;
int id=q.top().second;
q.pop();tag[id]=1;
}
return 1;
}
signed main()
{
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
cin>>n;n*=2;
for(int i=1;i<=n;i++) scanf("%d",&a[i].x);
for(int i=1;i<=n;i++) scanf("%d",&a[i].y);
for(int i=1;i<=n;i++) a[i].id=i,b[i]=a[i];
sort(a+1,a+n+1,cmp1);
sort(b+1,b+n+1,cmp2);
int l=1,r=2e9+7,ans=r;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans;
return 0;
}
反思
这个题和昨天那个题的贪心思路都是最普通的“每时每刻保证当前选择最优”,但是这两个题都涉及到“一个点可以去配对别的也可以被配对;那么我要是被配对了我不就没法配对别的了”这个问题。
在代码中,体现为判断tag[a[i].id]的语句带有后效性。
所以其实需要更加严谨的证明。注意以下证明比这个题强,证明它求出的确实是最多配对。
考虑操作:现在有一个集合 \(S\),每次选一个元素 \(i\),将它和 \(j\) 匹配并删掉这两个元素,产生一个贡献。假设 \(S-i\) 最多能产生 \(x\) 贡献,那么 \(S-i-j\) 至少最多产生 \(x-1\) 贡献,也就是说进行匹配不会让答案减少。所以存在 \(j\) 能和 \(i\) 匹配时一定会选择一个 \(j\) 匹配。
那么为什么选 \(a_j\) 最大的 \(j\) 呢?考虑如果选另一个 \(k\),只需要证明 \(S-i-j\) 的贡献不小于 \(S-i-k\) 的贡献即可。
也就是说 \(S-i-k\) 集合中去掉 \(j\) 加入 \(k\) 匹配数不会减少。如果 \(j\) 在此集合中没有匹配则显然;否则若 \(j\) 匹配 \(p\),只需要证明 \(k\) 也可以匹配 \(p\)。
首先我们选的是 \(a_i\) 最大的 \(i\),有 \(a_i+b_k\le mx\),所以有 \(a_p+b_k\le mx\)。
其次,由于 \(a_j\) 选了最大的,有 \(a_k\le a_j\)。由于 \(j,p\) 可以匹配,所以 \(a_j+b_p\le mx\),所以 \(a_k+b_p\le mx\)。
而且由于 \(k,p\) 都能参与匹配,有 \(a_k+b_k,a_p+b_p\le mx\) ,所以 \(k,p\) 显然可以匹配。
这下应该无论如何都是对的了。
嘛,启示是稍微有点后效性的贪心也是可以证明的。

浙公网安备 33010602011771号