Skip to content

峡国西高校 (easy version) - 题解

标签与难度

标签: 数学, 进制转换, 模拟, 大数处理 难度: 1200

题目大意喵~

主人你好呀,喵~ 这道题是关于一种特殊的运算规则哦!

我们有两个正整数 AABB,还有一个进制数 kk。题目定义了一种叫做“kk进制异或和”的运算,它其实就是在 kk 进制下的“不进位加法”。

具体来说,就是要把 AABB 都转换成 kk 进制数,然后把它们对应位上的数字相加,再对 kk 取模,得到结果的对应位数字。最后,再把这个 kk 进制的结果转换回十进制,就是我们要求的答案啦!

举个栗子🌰:如果 A=7,B=8,k=3A=7, B=8, k=3

  1. 先把它们变成三进制数:
    • 7=231+130=(21)37 = 2 \cdot 3^1 + 1 \cdot 3^0 = (21)_3
    • 8=231+230=(22)38 = 2 \cdot 3^1 + 2 \cdot 3^0 = (22)_3
  2. 然后进行不进位加法:
      (2 1)_3
    + (2 2)_3
    ---------- (对 3 取模)
      (1 0)_3
    • 个位:(1+2)(mod3)=0(1+2) \pmod 3 = 0
    • 高位:(2+2)(mod3)=1(2+2) \pmod 3 = 1
  3. 得到的结果是 (10)3(10)_3
  4. 最后把 (10)3(10)_3 变回十进制:131+030=31 \cdot 3^1 + 0 \cdot 3^0 = 3。 所以,答案就是 3 啦,是不是很有趣呢,喵~?

解题思路分析

这道题的核心就是模拟这个“kk进制不进位加法”的过程,呐。

一个很自然的想法是:

  1. 写一个函数,把十进制的 AABB 分别转换成 kk 进制,用数组存起来。
  2. 对齐这两个数组(在短的那个前面补0),然后逐位相加再对 kk 取模,得到一个新的结果数组。
  3. 再写一个函数,把这个结果数组从 kk 进制转换回十进制。

这个方法当然是可以的,但我有更直接、更优雅的办法哦!我们可以把“取位”、“计算”、“累加”这三个步骤合并在一个循环里完成,这样就不需要额外的数组来存储中间结果了,既省空间又快捷,喵~

我们可以想一下,一个十进制数 NNkk 进制下的最低位(个位)是什么呢?就是 N(modk)N \pmod k 呀!那去掉最低位后的数又是什么呢?就是 N/kN / k(整除)嘛!

利用这个性质,我们可以这样做:

  1. 我们设置一个变量 result 来存放最终的十进制答案,初始为0。
  2. 再设置一个变量 power_of_k,它代表当前处理的是 kk 进制的哪一位的权重。一开始是最低位,所以 power_of_k 初始为 11 (也就是 k0k^0)。
  3. 然后我们开始一个循环,只要 AA 或者 BB 还不为0,就说明还有位要处理:
    • 取出 AAkk 进制下的当前最低位:digit_a = A % k
    • 取出 BBkk 进制下的当前最低位:digit_b = B % k
    • 计算结果在这一位上的数字:new_digit = (digit_a + digit_b) % k
    • 把这一位对最终答案的贡献加到 result 上:result += new_digit * power_of_k
    • 处理完这一位后,我们要“削掉” AABB 的最低位,准备处理下一位:A /= kB /= k
    • 同时,下一位的权重也变了,所以要更新 power_of_kpower_of_k *= k
  4. AABB 都变成0时,循环结束,result 里就是我们想要的答案啦!

这个过程就像我们用爪子从右到左,一位一位地把两个数的 kk 进制表示扒拉出来,算出新的一位,然后立刻把它按权重加到最终结果里。这样一步到位,非常高效的说!

另外,题目中 AABB 的值最大可以到 101810^{18},所以我们在用 C++ 写代码的时候,一定要用 long long 类型来存它们和结果,不然会溢出导致答案错误哦,这是个小陷阱,要小心喵!

代码实现

这是我根据上面的思路,精心为你准备的C++代码,注释超详细的哦!

cpp
#include <iostream>

// 这是一个乐于助人的我为你写的代码,喵~

int main() {
    // 为了让输入输出更快一点,这是个小魔法!
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    long long k, a, b;
    // 从标准输入读取进制k和两个数字a, b
    std::cin >> k >> a >> b;

    // result 用来存储最终的十进制结果
    long long result = 0;
    // power_of_k 代表当前处理的位的权重,即 k^0, k^1, k^2, ...
    long long power_of_k = 1;

    // 只要 a 或 b 中还有未处理的位,就继续循环
    while (a > 0 || b > 0) {
        // 1. 获取 a 和 b 在 k 进制下的当前最低位
        long long digit_a = a % k;
        long long digit_b = b % k;

        // 2. 计算 k 进制不进位加法的结果位
        long long new_digit = (digit_a + digit_b) % k;

        // 3. 将这一位的值加到最终结果中
        //    new_digit 是当前位的数字,power_of_k 是它的权重
        result += new_digit * power_of_k;

        // 4. "削掉" a 和 b 的最低位,准备处理下一位
        a /= k;
        b /= k;

        // 5. 更新权重,为下一位做准备
        power_of_k *= k;
    }

    // 输出最终计算出的权重
    std::cout << result << std::endl;

    return 0;
}

复杂度分析

  • 时间复杂度: O(logk(max(A,B)))O(\log_k(\max(A, B))) 我们的循环在每次迭代时都会将 AABB 除以 kk。循环的次数取决于 AABB 中较大者在 kk 进制下的位数。一个数 NNkk 进制下的位数大约是 logkN\log_k N。所以,时间复杂度就是 O(logk(max(A,B)))O(\log_k(\max(A, B))),这是一个非常快的速度呢,喵!

  • 空间复杂度: O(1)O(1) 我们在算法中只使用了几个固定数量的变量(k, a, b, result, power_of_k),没有使用任何随输入规模增大的数据结构(比如数组)。所以,额外占用的空间是常数级别的,空间复杂度是 O(1)O(1) 的说。

知识点总结

这道题虽然简单,但也是对基本功的一次很好的检验呢!

  1. 进制转换思想: 核心思想是理解不同进制之间转换的原理,特别是如何通过取模和整除操作来逐位处理一个数。
  2. 模拟: 题目给出了一个明确的计算规则,我们只需要按照规则一步步实现即可。这是算法竞赛中很常见的一类问题。
  3. 迭代计算: 通过一个循环,同时进行“拆解”和“构建”的操作,避免了创建庞大的中间数据结构,是一种非常高效和优雅的编程技巧。
  4. 大数处理: 注意到输入数据的范围(101810^{18}),并选择合适的整数类型(C++中的 long long)是成功解题的关键一步,否则很容易因为溢出而得到错误答案,要时刻保持警惕哦,喵~

希望这篇题解能帮到你!继续加油,探索更多算法的奥秘吧,喵~!