2023年牛客多校第1场 H题 Matches

H.Matches

题意

有两个数列 a , b a,b a,b,现在可以选择其中一个数列,交换其中两个数字,最多可以操作一次,问操作后最小的 ∑ i = 1 n = ∣ a [ i ] − b [ i ] ∣ \sum_{i=1}^n=|a[i]-b[i]| i=1n=a[i]b[i]
数据范围: 1 < = n < = 2 5 , 0 < = ∣ a [ i ] ∣ , ∣ b [ i ] ∣ < = 1 0 9 1<=n<=2^5,0<=|a[i]|,|b[i]|<=10^9 1<=n<=25,0<=a[i],b[i]<=109

解题思路

做这题要先知道正序和反序
定义:
对于一对 ( a [ x ] , b [ x ] ) (a[x],b[x]) (a[x],b[x]) ( a [ y ] , b [ y ] ) (a[y],b[y]) (a[y],b[y]),不妨设a[x]< b[x],若a[y]> b[y]则称他们成反序关系,若a[y]< b[y]则为正序。
相交:部分重合,但各自有独立部分
包络:一部分完全在另一部分范围,完全包络
不交:完全没有重合部分
交换后的改变量为 ∣ a [ x ] − b [ x ] ∣ + ∣ a [ y ] − b [ y ] ∣ − ∣ a [ x ] − b [ y ] ∣ − ∣ a [ y ] − b [ x ] ∣ |a[x]-b[x]|+|a[y]-b[y]|-|a[x]-b[y]|-|a[y]-b[x]| a[x]b[x]+a[y]b[y]a[x]b[y]a[y]b[x]
要求sum最小,即该变量要最大

用图片表示交换a[x],a[y]后的关系

情况1:正序相交——>正序包络

在这里插入图片描述

改变量为0

情况2:反序包络——>反序不交

在这里插入图片描述

改变量>0

情况3:反序相交——>反序不交

在这里插入图片描述

改变量>0

改变量的值就是重合部分的两倍

综上:只需要在反序之间找到重合部分的最大值

把a[x]< b[x]为A序列,a[y]> b[y]为B序列

分为两个循环情况找重合部分

情况1

A序列在前,B序列在后

		b[y]		a[y]
		——————————————
——————————————
a[x]		b[x]
情况2

B序列在前,A序列在后

		a[x]		b[x]
		——————————————
——————————————
b[y]		a[y]

分别找到最大的重合部分

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
#define pII pair<int,int>
const int N=1e6+6;
int n;
int sA, sB;
vector<pII>A, B; 
int a[N], b[N];

signed main()
{
	
	cin >> n;
	int res = 0;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= n; i++) cin >> b[i];
	for (int i = 1; i <= n; i++)
	{
		res += abs(a[i] - b[i]);
		if (a[i] < b[i]) A.push_back(make_pair(a[i], b[i]));
		if (b[i] < a[i]) B.push_back(make_pair(b[i], a[i]));
	}
	sA = A.size();
	sB = B.size();
	sort(A.begin(), A.end());
	sort(B.begin(), B.end());
	int mx = -2e9;
	int sum = res;
	//cout << sum << '\n';
	
	for (int i = 0; i < sA; i++)
	{
		for (int j = 0; j < sB && B[j].first <= A[i].first; j++)//要有相交部分 
		{
			mx = max(mx, B[j].second);//相交的部分最大化 
		}
		if (mx >= A[i].first)
			res = min(res, sum - 2 * (min(mx, A[i].second) - A[i].first));
	}
	mx = -2e9;
	cout<<res <<' ';
	for (int i = 0; i < sB; i++)
	{
		for (int j = 0; j < sA && A[j].first <= B[i].first; j++)//要有相交部分 
		{
			mx = max(mx, A[j].second);//相交的部分最大化 

		}
		if (mx >= B[i].first)
			res = min(res, sum - 2 * (min(mx, B[i].second) - B[i].first));//减掉重合部分的两倍	
	}
	cout << res << '\n';
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值