题解:洛谷 AT_abc443_b Setsubun

【题目来源】

洛谷:AT_abc443_b [ABC443B] Setsubun - 洛谷(luogu.com.cn)

【题目描述】

在一年一度的 Setsubun 节上,人们要吃与自己年龄相同数量的豆子。高桥君在其他任何时候都不吃豆子。

他在今年的 Setsubun 节时 \(N\) 岁(\(0\) 年后)。

从这一年开始(包括这一年),他最早会在多少年后吃掉 \(K\) 或更多粒豆子?

他是不死之身,所以从今年开始,他最终一定会吃掉 \(K\) 或更多粒豆子。

【输入】

输入内容按如下格式给出:

\(N\) \(K\)

【输出】

输出一行表示答案。

【输入样例】

4 43

【输出样例】

6

【核心思想】

  1. 问题分析:高桥从 \(N\) 岁开始在每年 Setsubun 节吃与自己年龄相同数量的豆子。求最早多少年后累计吃掉 \(K\) 或更多粒豆子。设经过 \(t\) 年,则第 \(i\) 年(\(0 \leq i \leq t\))吃 \(N + i\) 粒豆子,累计吃的豆子数为 \(\sum_{i=0}^{t}(N + i) = (t+1)N + \frac{t(t+1)}{2} = (t+1)(N + \frac{t}{2})\)。这是一个整数二分查找问题,核心在于找到满足累计豆子数 \(\geq K\) 的最小 \(t\)

  2. 算法选择

    • 整数二分:在答案范围 \([0, 10^8]\) 内二分查找最小的 \(t\),使得累计豆子数 \(\geq K\)
  3. 关键步骤

    • 读入数据\(N\)\(K\)
    • 二分查找\(l = 0, r = 10^8\)):
      • \(mid = (l + r) / 2\)
      • 计算累计豆子数:\(ans = mid \times (mid + 1) / 2 + N \times (mid + 1)\)
      • \(ans \geq K\)\(r = mid\)
      • 否则:\(l = mid + 1\)
    • 输出结果\(l\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(\log 10^8) \approx O(27)\),二分查找的迭代次数
    • 空间复杂度:\(O(1)\)
  5. 整数二分与等差数列求和的核心思想

    • 累计公式的推导:经过 \(t\) 年,共 \(t+1\) 个 Setsubun 节(包括今年)。年龄序列为 \(N, N+1, \ldots, N+t\),是首项 \(N\)、末项 \(N+t\)、项数 \(t+1\) 的等差数列。和 = \(\frac{(t+1)(2N + t)}{2} = (t+1)N + \frac{t(t+1)}{2}\),与代码中的 mid*(mid+1)/2 + n*(mid+1) 一致(其中 \(mid = t\)
    • 单调性保证:累计豆子数随 \(t\) 严格递增,满足二分查找的单调性条件
    • 整数二分的边界处理while (l < r) 配合 r = midl = mid + 1,最终 \(l = r\) 即为最小满足条件的 \(t\)
    • 不死之身的保证:题目保证最终一定能达到 \(K\),因此二分范围只需足够大即可,无需处理无解情况
    • 适用于等差数列求和、单调函数求根、整数二分查找类问题

【算法标签】

入门 #整数二分

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long  // 将int定义为long long类型,避免大数溢出

int n, k;  // n: 初始值,k: 目标值

// 检查函数:判断mid是否满足条件
bool check(int mid)
{
    // 计算公式:mid*(mid+1)/2 + n*(mid+1)
    // 第一部分:1到mid的等差数列和
    // 第二部分:n重复(mid+1)次的和
    int ans = 0;
    ans = mid * (mid + 1) / 2 + n * (mid + 1);
    return ans >= k;  // 判断结果是否达到目标k
}

signed main()  // 使用signed main()替代int main(),因为int被重定义为long long
{
    cin >> n >> k;  // 读入初始值n和目标值k

    // 二分查找范围
    int l = 0, r = 100000000;

    // 二分查找
    while (l < r)
    {
        int mid = (l + r) / 2;
        if (check(mid))  // 如果mid满足条件
        {
            r = mid;  // 缩小右边界
        }
        else
        {
            l = mid + 1;  // 增大左边界
        }
    }

    // 输出最小满足条件的值
    cout << l << endl;

    return 0;
}

【运行结果】

4 43
6
posted @ 2026-08-27 11:06  团爸讲算法  阅读(4)  评论(0)    收藏  举报