1 条题解

  • 0
    @ 2026-4-3 13:45:33

    A. 求最少操作次数 - 详细题解

    问题重述

    给定两个整数 nnkk,每次操作可以减去 kk 的任意非负整数次幂(即 k0,k1,k2,k^0, k^1, k^2, \dots)。求将 nn 变为 00 所需的最少操作次数。


    关键观察

    观察 1:k=1k = 1 的特殊情况

    k=1k = 1 时,kk 的幂次只有 10=11^0 = 1。因此每次只能减去 11,最少操作次数为:

    ans=n\text{ans} = n

    观察 2:模 kk 的视角

    考虑对 kk 取模。任何 kxk^xx1x \ge 1)都是 kk 的倍数,因此:

    kx0(modk)(x1)k^x \equiv 0 \pmod{k} \quad (x \ge 1)

    k0=1k^0 = 1,所以:

    11(modk)1 \equiv 1 \pmod{k}

    因此,只有减去 k0=1k^0 = 1 的操作会改变 nnkk 的值


    观察 3:处理余数

    n=qk+rn = q \cdot k + r,其中 0r<k0 \le r < k

    为了将 nn 变为 0000kk 的倍数),我们需要消除余数 rr

    由于只有减去 11 才能改变余数,我们必须执行 rr 次减去 11 的操作,使 nn 变为 kk 的倍数:

    nnr=qkn \to n - r = q \cdot k

    rr 次操作是必须的。


    观察 4:转化为子问题

    现在 n=qkn' = q \cdot kkk 的倍数。考虑以下两种策略:

    策略 A:继续减去 11k0k^0

    • 减去 11 后,nn' 变为 qk1q \cdot k - 1,余数变为 k1k-1
    • 需要再减去 k1k-111 才能回到 kk 的倍数
    • 总共 kk 次操作,效果相当于减去 kk(即减去 k1k^1 一次)

    策略 B:直接减去 k1=kk^1 = k

    • 一次操作,nn' 变为 (q1)k(q-1) \cdot k

    显然,策略 B 更优(11 次操作 vs kk 次操作)。

    因此,nnkk 的倍数时,最优策略是不断减去 kk 本身,相当于将问题规模缩小为 n/kn/k


    观察 5:递归/迭代过程

    将上述过程形式化:

    1. 设当前值为 nn
    2. n=0n = 0,结束
    3. 否则,取 r=nmodkr = n \bmod k,需要 rr 次减去 11 的操作
    4. 然后令 n=n/kn = \lfloor n/k \rfloor,重复步骤 2-4

    这实际上就是nn 表示为 kk 进制数,然后求各位数字之和


    数学证明

    nnkk 进制表示为:

    $$n = d_0 + d_1 \cdot k + d_2 \cdot k^2 + \cdots + d_m \cdot k^m $$

    其中 0di<k0 \le d_i < k

    • d0d_0nmodkn \bmod k,需要 d0d_0 次减去 11 的操作
    • 减去 d0d_011 后,nn 变为 k(d1+d2k+)k \cdot (d_1 + d_2 \cdot k + \cdots)
    • 然后可以用一次减去 kk 的操作替代 kk 次减去 11 的操作
    • 这等价于将 nn 除以 kk 后继续处理

    因此,最少操作次数为:

    $$\text{ans} = d_0 + d_1 + d_2 + \cdots + d_m = \sum_{i=0}^{m} d_i $$

    nnkk 进制下的各位数字之和。


    算法步骤

    1. 如果 k=1k = 1,直接输出 nn
    2. 否则,初始化 ans=0ans = 0
    3. n>0n > 0 时:
      • ans=ans+(nmodk)ans = ans + (n \bmod k)
      • n=n/kn = \lfloor n/k \rfloor
    4. 输出 ansans

    完整代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int find_min_operations(int n, int k) {
        if (k == 1) return n;
        
        int ans = 0;
        while (n > 0) {
            ans += n % k;
            n /= k;
        }
        return ans;
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(0);
        
        int t;
        cin >> t;
        
        while (t--) {
            int n, k;
            cin >> n >> k;
            cout << find_min_operations(n, k) << "\n";
        }
        
        return 0;
    }
    

    时间复杂度

    • 每个测试用例:O(logkn)O(\log_k n)
    • 总复杂度:O(tlogkn)O(t \cdot \log_k n),其中 t104t \le 10^4

    示例验证

    测试用例 1:n=5,k=2n = 5, k = 2

    55 的二进制表示为 1012101_2,各位数字和 =1+0+1=2= 1 + 0 + 1 = 2

    输出:22

    测试用例 2:n=3,k=5n = 3, k = 5

    33 的五进制表示为 353_5,各位数字和 =3= 3

    输出:33

    测试用例 3:n=16,k=4n = 16, k = 4

    1616 的四进制表示为 1004100_4,各位数字和 =1+0+0=1= 1 + 0 + 0 = 1

    输出:11

    测试用例 4:n=100,k=3n = 100, k = 3

    100100 的三进制表示:100÷3=33100 \div 3 = 331133÷3=1133 \div 3 = 110011÷3=311 \div 3 = 3223÷3=13 \div 3 = 1001÷3=01 \div 3 = 011

    得到数字:1,0,2,0,11, 0, 2, 0, 1,从低位到高位:10201310201_3

    各位数字和 =1+0+2+0+1=4= 1 + 0 + 2 + 0 + 1 = 4

    输出:44

    测试用例 5:n=6492,k=10n = 6492, k = 10

    64926492 的十进制各位数字和 =6+4+9+2=21= 6 + 4 + 9 + 2 = 21

    输出:2121

    测试用例 6:n=10,k=1n = 10, k = 1

    k=1k = 1,直接输出 n=10n = 10

    输出:1010


    关键公式总结

    $$\boxed{\text{最少操作次数} = \text{将 } n \text{ 表示为 } k \text{ 进制后的各位数字之和}} $$特殊情况:当 k=1 时,答案为 n\boxed{\text{特殊情况:当 } k = 1 \text{ 时,答案为 } n}
    • 1

    信息

    ID
    6333
    时间
    1000ms
    内存
    256MiB
    难度
    2
    标签
    递交数
    1
    已通过
    1
    上传者