1 条题解

  • 0
    @ 2026-9-6 20:54:55

    题解

    需要判断 nn 是否能表示为恰好 kk 个数之和,其中每个数都是 1,3,9,1,3,9,\ldots 中的一个,且同一个数可以重复使用.换句话说,我们只关心一个整数能否表示成指定项数的 33 的幂之和.

    算法 0:按总人数和队伍数动态规划

    nn 很小时,令 fs,cf_{s,c} 表示是否能用恰好 cc 个三次幂凑出总和 ss.从 f0,0=1f_{0,0}=1 出发,枚举要加入的三次幂并转移到 fs+3x,c+1f_{s+3^x,c+1}

    若直接实现,单组询问的时间复杂度为 O(nklogn)O(nk\log n),空间复杂度为 O(nk)O(nk)

    预期通过 subtask 1、2,预期得分为 2525 分.

    算法 1:特殊性质

    k2k\le2 时:若 k=1k=1,直接判断 nn 是否为 33 的幂;若 k=2k=2,枚举第一项 3x3^x,再判断 n3xn-3^x 是否为 33 的幂.朴素实现的时间复杂度为 O(log2n)O(\log^2 n),也可以预处理所有不超过 101810^{18}33 的幂后用集合查询做到 O(logn)O(\log n).预期通过 subtask 3,预期得分为 1515 分.

    n=3xn=3^x 时,最初可以只有一个大小为 3x3^x 的小队.把一个大小为 3y3^y 的小队拆成三个大小为 3y13^{y-1} 的小队,会使小队数量增加 22.因此恰好可以得到所有不超过 nn 的奇数队伍数.预期通过 subtask 4,预期得分为 2020 分.

    算法 2:三进制数位和

    nn 写成标准三进制形式:

    n=x0cx3x,cx{0,1,2}.n=\sum_{x\ge0}c_x3^x,\qquad c_x\in\{0,1,2\}.

    s=x0cxs=\sum_{x\ge0}c_x

    为三进制数位和.标准三进制展开本身给出了一个使用 ss 个三次幂的方案.

    如果某个表示中同一位出现至少三个 3x3^x,就可以把它们合并成一个 3x+13^{x+1},项数减少 22.持续合并最终必然得到唯一的标准三进制展开,所以任何表示使用的项数都不小于 ss,并且与 ss 奇偶性相同.

    另一方面,从标准展开开始,只要当前方案中还存在一个大于 11 的项,就可以将一个 3x3^x 拆成三个 3x13^{x-1}.每次拆分使项数恰好增加 22;持续拆分最终会得到 nn11.因此拆分过程依次取得 s,s+2,,ns,s+2,\ldots,n 中的每一个项数,不会跳过任何与 ss 同奇偶的候选值.

    因此答案为 Yes 当且仅当

    skn(ks)mod2=0.s\le k\le n\quad\text{且}\quad (k-s)\bmod2=0.

    正确性证明

    上述合并说明,所有合法表示的项数必须至少为 ss,且与 ss 同奇偶;显然项数也不会超过全为 11 时的 nn.因此条件是必要的.上述逐次拆分构造会取得区间 [s,n][s,n] 内所有与 ss 同奇偶的项数,因此条件也是充分的.算法按照这个充要条件判断,故答案正确.

    每组询问只需枚举 nn 的三进制数位,时间复杂度为 O(log3n)O(\log_3 n),空间复杂度为 O(1)O(1).全部询问的时间复杂度为 O(Tlogn)O(T\log n).实现时直接反复对 nn 除以 33,不需要显式生成更大的 33 的幂,因此使用有符号 6464 位整数即可安全处理 n1018n\le10^{18}

    预期通过所有 subtask,预期得分为 100100 分.

    参考代码

    #include <cstdint>
    #include <iostream>
    
    int main() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
    
        int test_cases;
        std::cin >> test_cases;
        while (test_cases--) {
            std::int64_t n, k;
            std::cin >> n >> k;
            std::int64_t digit_sum = 0;
            for (std::int64_t x = n; x > 0; x /= 3) {
                digit_sum += x % 3;
            }
            bool possible = digit_sum <= k && k <= n && ((k - digit_sum) % 2 == 0);
            std::cout << (possible ? "Yes" : "No") << '\n';
        }
        return 0;
    }
    
    • 1

    信息

    ID
    257
    时间
    2000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    29
    已通过
    4
    上传者