TonyYin's Blog

Back

题意#

给定 2n2n 个点,其中 nn 个在 xx 轴上,另外 nn 个在 yy 轴上。现在要求每一个 xx 轴上的点与一个 yy 轴上的点连线,每个点恰好被连线一次。求所有线段的欧几里得距离之和

分析#

首先根据数据范围 n105n\leq 10^5,在考场可以判断出贪心或二分。

之后看到样例解释的配图,发现可以抽象出:对于两条线段的模型。

由上图可以看出,对于四个点,只有两种连线方法:不交叉或交叉。

不交叉的欧几里得距离和为:AB+CD|AB|+|CD|交叉的欧几里得距离为 AC+BD|AC|+|BD|.

经过肉眼观测,我们可以用小学数学证明不交叉一定优于交叉

由于三角形任意两边之和大于第三边,所以 AB<AO+BO|AB|<|AO|+|BO|CD<CO+DO|CD|<|CO|+|DO|.

于是 AB+CD<AO+BO+CO+DO=AC+BD|AB|+|CD|<|AO|+|BO|+|CO|+|DO|=|AC|+|BD|.

将结论推广到 nn 条线段,发现所有线段都不交叉是最优的,否则将交叉的两个线段交换,可以使距离和更小。

考虑如何按照这个方法,对所有点进行两两配对。

容易想到,把所有处于负半轴上的点,对称到正半轴上。这样处理后距离和不变。

所以,把 xx 轴上的点和 yy 轴上的点分别按照横竖坐标升序排序,排名相同的点进行配对,一定不会交叉。

代码#

考场代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e5 + 10;
int T;
double a[MAXN], b[MAXN];
signed main() {
    cin >> T;
    while(T--) {
        int n, cnt1 = 0, cnt2 = 0; cin >> n;
        for(int i = 1; i <= 2 * n; i++) {
            int x, y; scanf("%lld%lld", &x, &y);
            if(x == 0) {
                if(y < 0) y *= -1;
                a[++cnt1] = y;
            }
            if(y == 0) {
                if(x < 0) x *= -1;
                b[++cnt2] = x;
            }
        }
        sort(a + 1, a + n + 1); sort(b + 1, b + n + 1);
        double ans = 0;
        for(int i = 1; i <= n; i++) {
            ans += sqrt(a[i] * a[i] + b[i] * b[i]);
        }
        printf("%.11lf\n", ans);
    }
    return 0;
}
cpp
【CodeForces-1495A】Diamond Miner
https://www.tonyyin.top/blog/oi-solution/cf1495a
Author TonyYin
Published at March 12, 2021
Comment seems to stuck. Try to refresh?✨