题解-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\) 显然可以匹配。

这下应该无论如何都是对的了。

嘛,启示是稍微有点后效性的贪心也是可以证明的。

posted @ 2025-07-25 15:59  cinccout  阅读(24)  评论(0)    收藏  举报