Skip to content

题解-Atcoder-abc174e

Posted on:2023年4月26日 at 11:35

本文章遵守知识共享协议 CC-BY-NC-SA ,同步发表于洛谷题解区,转载时须在文章的任一位置附上原文链接和作者署名(rickyxrc)。推荐在我的个人博客阅读。

题面大意

给定 nn 个数 A1,A2,,AnA_1,A_2,\cdots,A_n,并给你 kk 次操作机会,每次你从 AA 中选择一个数 aAa\in A 并指定一个实数 t(0,a)t \in (0,a),在 AA 中删除 aa 并加入 ttata-t,求使得 AA 中最大值最小的最小值。

图形化理解:给你 nn 条绳子,你需要切 kk 刀,求最长长度最短可以被切成多少。

解法

显然,我切的刀数越多,最长长度就会越小。

考虑二分答案,二分那个最大值,检验使得所有的值小于给定值的方案数能否小于要求的 kk 次,如果满足继续缩小右边界,不满足扩大左边界,直到两边界趋近于一个值为止。

为什么想到二分答案?

通常,我们会一个一个判断给定条件是否合法,复杂度为 O(n2)O(n^2),也就是遍历和检查的复杂度乘积。

而这里,题目的问题具有单调性,就是上文所说的切的越多,最长长度越小。

考虑二分答案,也就是二分可以的最长长度,计算出让所有长度小于那个长度的需要刀数,判断是否合法,再二分,直到找到答案为止。

我们要将一个数 xx 切成不大于 ll 的段,那么每次切的长度最大可以为 ll,最少要切 xl1\lceil \frac{x}{l} \rceil-1 段,我们要判断是否合法,只需要计算 i=1nxil1\sum_{i=1}^n \lceil \frac{x_i}{l} \rceil-1 是否小于 kk 即可。

判断答案是否合法的函数就可以实现如下:

// 这里是指切成小于val的段的总刀数是否满足
bool check(double val)
{
  long long res = 0;
  for (long long i = 1; i <= n; i++)
    if (lenn[i] - val > 1e-3) // 注意精度
      res += (long long)(ceil(double(lenn[i]) / val)) - 1;
  return res <= k;
}

主函数也很好实现,但是记得注意精度!!

#include <stdio.h>
#include <math.h>
#define maxn 200007

long long lenn[maxn];
long long n, v, k, mx;
long long max(long long a, long long b)
{
  return a > b ? a : b;
}

signed main()
{
  scanf("%lld%lld", &n, &k);
  for (long long i = 1; i <= n; i++)
    scanf("%lld", lenn + i),
        mx = max(mx, lenn[i]);

  double l = 0, r = mx, mid;
  while (r - l > 1e-6)
  {
    mid = (l + r) / 2.0;
    if (check(mid))
      r = mid;
    else
      l = mid;
  }

  printf("%d", (long long)(mid + 1));
  return 0;
}


在 Rickyxrc's blog 出现的文章,若无特殊注明,均采用 CC BY-NC-SA 4.0 协议共享,也就是转载时需要注明本文章的地址,并且引用本文章的文章也要使用相同的方式共享。