思路

首先需要明确几个关键概念:

  • 圆包含原点的判定:若圆心 (x,y) 到原点的距离 d = √(x²+y²) 小于半径 r,则该圆包含原点;否则不包含。
  • 移动代价计算:代价 = 圆面积 × 移动距离。圆面积为 πr²,移动距离是让圆不再包含原点所需的最小距离。
  • 最小代价策略:对于原本包含原点的圆,若需要让它不包含原点,只需将其向任意方向移动 (r - d) 的距离(这是最小移动距离);对于原本不包含原点的圆,无需移动。

要让总代价最小,需遵循 “最小代价优先保留” 原则:

  1. 先筛选出所有原本包含原点的圆,计算每个圆被移出原点的代价 (r-d)×πr²
  2. 将这些代价从大到小排序;
  3. 保留代价最大的 k 个圆(即不移动这 k 个圆,让它们继续包含原点),移动剩余的圆(代价累加),最终总代价即为最小值。

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
#define all(a) a.begin(), a.end()
const int M = 1e5 + 3;
const int mod = 80112002; 
const double pi = acos(-1.0); // 高精度π值

void solve()
{
    int n, k;
    cin >> n >> k;
    vector<double> arr; // 存储需要移动的圆的代价
    double sum = 0;   // 总代价

    for (int i = 1; i <= n; i++)
    {
        double x, y, r;
        cin >> x >> y >> r;
        // 计算圆心到原点的距离d
        double d = sqrt(x * x + y * y);
        // 若圆包含原点,计算移出代价并加入列表
        if (d < r)
            arr.push_back((r - d) * pi * r * r);
    }

    //代价从大到小排序,保留前k个(不移动),移动剩余的
    sort(all(arr), greater<double>());
    // 累加第k个及之后的代价(需要移动的圆的总代价)
    for (int i = k; i < arr.size(); i++)
        sum += arr[i];
    printf("%.8lf\n", sum);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int qwq = 1; 
    while (qwq--)
    {
        solve();
    }
    return 0;
}

复杂度分析

  • 时间复杂度:O (n log n)(主要来自排序);
  • 空间复杂度:O (n)(存储代价列表)。