从CF1901C学习除二向下取整的思路

https://codeforces.com/problemset/problem/1901/C

我发现对于向下取整向上取整的题目(不特指除二),没有一些常见的思路积累。

1|0Description

给定一个长度为 n 的序列 {an}。每次操作你需要选择一个整数 x 并将所有 ai 替换为 ai+x2。求至少多少次操作后能将所有 ai 变相同。

若最少次数小于等于 n,输出操作次数和每次操作所选择的 x。否则仅输出操作次数。

1n2×1050ai109

2|0Solution

可以观察到,每次操作后,数字的相对大小关系不会发生改变。

例如 a={4,2,5}x=3,那么操作后的 a={3,2,4}。可以发现 a3a3 分别都是两个序列的最大值,a2a2 都是两个序列的最小值。

那么就启发我们不用同时维护原序列中的所有值,只需要维护序列中的最大值和最小值。当这两个值相等时,就代表序列中所有的数都相同了。

那么问题就被转化成了这样:

给定两个整数 a,b。每次操作你需要选择一个整数 x 并将 aa+x2bb+x2。求至少多少次操作后 a=b

每次操作我们肯定是希望利用下取整这个优秀性质来让 a 尽量接近 b,也就是想让操作后 ab 尽量小。那么我们可以直接分讨:

然后就解决了。用 vector 记录答案即可。

3|0Code

void solve() { cin >> n; a = 0, b = 2e9; for (int i = 1; i <= n; ++ i ) { cin >> x; a = max(a, x); b = min(b, x); } vector<int> res; while (a != b) { if (a % 2 == 0 && b % 2 == 0) x = rand(); else if (a % 2 == 1 && b % 2 == 1) x = rand(); else if (a % 2 == 0 && b % 2 == 1) x = rand() * 2 + 1; else x = rand() * 2; a = (a + x) / 2; b = (b + x) / 2; res.push_back(x); } cout << res.size() << '\n'; if (res.size() <= n) { for (auto i : res) cout << i << ' '; if (res.size()) puts(""); } return; }

题解的四个超链接很重要,对于除以二的向下取整的值,可以直接把奇数的转成减一除二

1|0a 为偶数,b 为偶数

Δ 为操作后 ab 的差。

  • 若选择的 x 为偶数: a=a+x2b=b+x2Δ=ab2。 -
  • 若选择的 x 为奇数: a=a+x12b=b+x12Δ=ab2
  • 可以发现,无论 x 怎样取值,两数差一定为 ab2。因此 x 任意取值。

4|0Solution

x 对于除以 k 上取整

sum = (x + k - 1) / k

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17978428.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示