农务署:题解¶
解法比较¶
出题人解法把有限小数和循环小数精确化为分数:有限部分使用十的幂作分母,循环部分使用 \(10^f(10^r-1)\) ,其中 \(f,r\) 是非循环位数和循环节长度。合并后约分,再求全部最简分母的最小公倍数。整数倍条件等价于能被每个最简分母整除,所以下文的证明也适用于出题人解法。
出题人实现自带以 \(10^9\) 为基底的大整数和试商除法;Codex(GPT-6)采用的实现改用较直接的十进制大整数运算,明确零、前导零和循环节的解析。两者都采用学校式乘除与欧几里得 GCD;用 \(D\) 表示中间整数的最大十进制位数,保守时间上界为 \(O(nD^3)\) ,算术工作空间为 \(O(D)\) .改变基底会改变常数,不能据此宣称提速;收益是可移植的精确运算与边界说明,数学模型未变。
将小数准确化为分数¶
不能使用浮点数读取比例。设有限部分的小数位数为 \(f\) ,循环部分长度为 \(r\) .把有限部分的小数点去掉,得到整数 \(A\) ;把循环部分接在这些数字后面,得到整数 \(B\) .则
证明:\(10^f x\) 与 \(10^{f+r}x\) 的无限小数尾部相同,相减后得到整数 \(B-A\) .该公式也适用于全零循环节、前导零和循环 \(9\) .例如 0.7 142857 给出 \((7142857-7)/(10(10^6-1))=5/7\) .
用最大公约数约分,得到 \(x=u/v\) .当分子为零时,\(\gcd(0,v)=v\) ,约分后的分母自然为 \(1\) .
总量是分母的最小公倍数¶
因为 \(u,v\) 互质,\(Hx\) 为整数当且仅当 \(v\mid H\) .因此答案为所有最简分母的最小公倍数,逐项更新:
这个条件也足够:已知作物数量均为整数,总和不超过 \(H\) ;剩余数量 \(H-\sum_i Hr_i\) 也是非负整数,可分配给未公布的作物。初始 \(H=1\) ,依次更新后得到最小可行正整数。
高精度实现¶
答案可能远超 64 位。参考程序仅用标准 C++17,实现非负十进制大整数的比较、减法、乘法、商和余数,无需外部大整数库。
长除法从高位逐位读入被除数。每读一位,余数乘以十再加这一位,反复减去除数得到当前商位。旧余数小于除数,所以新商位最多为九,循环不会随数值大小线性增长。除法只用于非零除数;欧几里得算法先判断第二个数是否为零。
设 \(D\) 为计算过程中大整数的最大十进制位数,\(L\) 为单个比例的最大输入长度,则 \(D=O(nL)\) .朴素乘法和单次长除法的时间均为 \(O(D^2)\) .欧几里得算法每两轮至少将较大参数缩小一半,因此最多 \(O(D)\) 轮,可得整体的保守时间上界 \(O(nD^3)\) ,空间 \(O(D)\) .这里把位数保留为参数,不把本题的长度上限代入复杂度。
验证¶
独立实现以 Python 精确分数把有限前缀与循环几何级数分别相加,再计算分母的最小公倍数。所有正式输入也以该实现核对答案,覆盖零比例、循环九、重复循环节、长有限部分及长循环部分。
程序与复现¶
参考程序见 std.cpp。使用 C++17 编译后,可用 tests/generate.py --out-dir <题目目录以外的路径> --std <程序路径> 生成输入和答案。固定种子与用例目的见 生成计划。性能测量见 评测环境与性能。