专栏文章

题解:P13591 [NWRRC 2023] Kitchen Timer

P13591题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mioi9utj
此快照首次捕获于
2025/12/02 19:39
3 个月前
此快照最后确认于
2025/12/02 19:39
3 个月前
查看原文

前言

蒟蒻心血来潮做的一道题,因为还没有通过一道题号数字有五位数的题。
翻译(参考机翻)

题目描述

Kenny 的厨房里有一个微波炉,配有一个奇怪的单按钮计时器界面。 当把食物放入微波炉并想开始加热时,你需要按下按钮一次或多次。第一次按下按钮时,计时器设置为 11 分钟。如果立即再次按下按钮,计时器会增加 22 分钟(总计 33 分钟)。紧接着再按一次,会增加 44 分钟,以此类推。如果连续按下按钮第 kk 次,它会增加 2k12^{k-1} 分钟。
仅通过连续按按钮无法设置某些时间(例如 22 分钟)。幸运的是,你可以通过暂停一秒钟来重置按钮计数器。例如:按一次、暂停一秒、再按一次,可以设置 2 分钟。另一个例子:按两次(1+21+2)、暂停、再按三次(1+2+41+2+4),总时间为 1+2+1+2+4=101+2+1+2+4=10 分钟。
Kenny 需要加热食物恰好 xx 分钟。请帮助他找到设置计时器所需的最少暂停次数(假设只有暂停消耗时间,按按钮的时间忽略)。

输入格式

  • 第一行包含测试用例数 tt1t1041 \le t \le 10^4)。
  • 接下来 tt 行,每行一个整数 xx1x10181 \le x \le 10^{18}),表示需要加热的分钟数。

输出格式

  • 对于每个测试用例,输出一个整数,表示最少暂停次数。

样例

说明/提示

  • x=1x=1:按一次,无需暂停。
  • x=2x=2:按、暂停、按,暂停 11 次。
  • x=3x=3:按两次,无需暂停。
  • x=4x=4:按两次、暂停、按一次,暂停 11 次。

思路

分析

As we all know, 连续kk 次按钮,增加的分钟数为 1+2+4++2k1=2k11+2+4+…+2^{k−1}=2^k−1。所以我们可以将 xx 看为 nn2k12^k-1 的和(n1n\ge1)。问题就转化为使 nn 最小化,所求的暂停数就等于 n1n-1

数学推导

蒟蒻数学不好,凑合看吧 QaQ
已知最少分段数为 nn,则暂停次数为 n1n−1。每段对应一连续按钮序列,其和为 2ki12^{k_i}−1 则:
x=i=1n(2ki1)=(i=1n2ki)nx=\sum_{i=1}^n(2^{k_i}-1)=(\sum_{i=1}^n2^{k_i})-n
移项得:
x+m=i=1n2kix+m=\sum_{i=1}^n2^{k_i}
其中 ki1k_i\ge1(即 2ki22^{k_i}\ge2)。

条件:

  1. x+nx+n 必须能表示为 mm22 的幂次(指数 1\ge1) 之和。
  2. 由于每个幂次至少为 22,有 x+m2mx+m\ge2m(即 xmx\ge m)。
  3. x+mx+m 的二进制表示中 11 的个数(记为 cntcnt)必须满足 cntmcnt\le m(因为重复幂次可通过拆分合并,例如 2k+2k=2k+12^k+2^k=2^{k+1},拆分操作可增加项数)。

算法

枚举即可。可以使用 GCC 内部函数快速统计 __builtin_popcountll() 一个二进制数 11 的个数。

复杂度

xx 最大 101810^{18},二进制最多不超过 6464 位,所以枚举到 7070 完全可以。

代码

CPP
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
int t;
int main()
{
    scanf("%d",&t);
    while (t--)
   {
        ll x;
        scanf("%lld",&x);
        // 枚举分段数 m (从 1 到 70)
        for (int n = 1; n <= 70; n++)
       {
            if (x < n) continue;  // 不满足基本条件 x ≥ m
            ll num = x + n;
            int cnt = __builtin_popcountll(num);  // 使用内置函数计算 1 的个数
            if (cnt <= n)
            {
                printf("%d\n",n-1);  // 暂停次数 = 分段数 -1
                break;
            }
        }
    }
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...