题解:P16427 「YLLOI-R4-T3」本草纲目

题目传送门

题目大意

初始有 \(1\) 个病毒群,里面有 \(1\) 个病毒。

可以做以下两种操作无限次:

  1. 把当前所有病毒群复制一份。
  2. 选择一个病毒群,让它的病毒数量乘以 \(x\)

问最终所有病毒群的病毒数量总和是否可以等于 \(n\)

思路

观察样例的第一组数据:\(n=12,x=2\)

样例解释给出病毒群序列:\((1) \rightarrow (1,1,1,1,1,1,1,1) \rightarrow (4,2,1,1,1,1,1,1)\)

由于复制操作不改变单个元素的值,所以可以证明无论如何操作,序列中的元素始终是 \(x\) 的幂次

考虑将 \(n\) 写成 \(x\) 进制。
\(12_{10}=1100_2\)\(1100_2\) 的各位数字之和 \(s=1+1+0+0=2\)。所以,我们至少要用到 \(2\)\(2\) 的幂次凑出 \(12\)。可以发现,\(s\) 就是这个最少次数。

证明 下述证明过程均基于 $x>1$ 讨论,$x=1$ 的情况在代码中进行了单独处理。

命题一:可以用 \(s\)\(x\) 的幂次表示出 \(n\)

\(n\)\(x\) 进制展开:

\(n=c_0x^0+c_1x^1+\cdots+c_mx^m\)

总共 \(\sum c_i = s\) 项,每一项都是 \(x\) 的幂次,所以用 \(s\) 项可以得到 \(n\)

命题二:用 \(s\)\(x\) 的幂次是最优的

考虑 \(x\) 进制下的进位过程。

任何由 \(x\) 的幂次组成的和 \(x^{a_1}+x^{a_2}+x^{a_3}+\cdots+x^{a_k}\),每位上有可能产生超过 \(x-1\) 个该幂次。不断将 \(x\)\(x^i\) 合并得到 \(1\)\(x^{i+1}\),即进位。

注意到每次进位项数都会减少 \(x-1\)

设一共产生了 \(t\) 次进位,则:
\(s=k-t(x-1)\)

因为 \(t \geq 0 \land x-1 \geq 1\),所以:
\(s=k-t(x-1) \leq k\)

\(k \geq s\)

因此任何拆分方案的项数 \(k\) 至少为 \(s\)

证毕


由于复制时元素个数翻倍,所以元素个数一定是 \(2\) 的幂次。

假设最终状态下元素的个数为 \(t\),则由上文证明,有 \(t \geq s\)

假设共做了 \(y\) 次复制,则 \(t=2^y\)

注意到每次将某数 \(a\) 进行倍增,总和增加 \(a(x-1)\)

由于 \(a\)\(x\) 的幂次,\(a(x-1)\)\((x-1)\) 的倍数。所以每次操作,总和的增量都是 \(x-1\) 的倍数。

因此,\(n\) 与操作前的总和 \(w\) 的差一定是 \((x-1)\) 的倍数,即:

\(n-w \equiv 0 \pmod{x-1}\)

所以:

\((n-w) \bmod (x-1)=0\)


综上,要得到 \(n\),当且仅当 \(\exists w=2^y\)\(y \geq 0\)),满足:

  • \(w \geq s\)
  • \((n-w) \bmod (x-1)=0\)

如果存在,输出 Yes,否则输出 No


\(x=1\) 的特殊情况

此时只需要考虑复制操作,所以最后的总和必然是 \(2\) 的幂次。

代码

AC Code
#include <iostream>
using namespace std;
int T,n,x;
int main()
{
    cin >> T;
    while(T--)
    {
        cin >> n >> x;
        // x=1的特殊情况 
        if(x==1)
        {
            if(!(n&(n-1)))
            {
                cout << "Yes\n";
            }
            else
            {
                cout << "No\n";
            }
            continue;
        }
        // 计算n在x进制下的数字和s 
        int s=0,k=n;
        while(k)
        {
            s+=k%x;
            k/=x;
        }
        // 寻找是否存在符合条件的w 
        bool f=0;
        for(int w=1;w<=n;w<<=1)
        {
            if(w>=s&&(n-w)%(x-1)==0)
            {
                f=1;
                break;
            }
        }
        cout << (f?"Yes\n":"No\n");
    }
    return 0;
}
posted @ 2026-05-15 22:46  kevin1426730  阅读(3)  评论(0)    收藏  举报