1 条题解
-
0
题解:投票人数统计问题
题目分析
我们有 个比例 ,它们四舍五入到小数点后 位。
我们需要找到最小的正整数 ,使得存在非负整数 满足:-
比例约束:
$$P_i - \frac12 \times 10^{-L} \le \frac{D_i}{K} < P_i + \frac12 \times 10^{-L} $$ -
整数约束: 是非负整数
-
总和约束:
数学转换
将比例约束转换为 的范围:
$$K \cdot \left(P_i - \frac12 \times 10^{-L}\right) \le D_i < K \cdot \left(P_i + \frac12 \times 10^{-L}\right) $$设:
- $L_i = \lceil K \cdot (P_i - 0.5 \times 10^{-L}) \rceil$(下界向上取整,因为 是整数)
- $R_i = \lfloor K \cdot (P_i + 0.5 \times 10^{-L}) \rfloor$(上界向下取整)
我们需要:
$$L_i \le D_i \le R_i \quad \text{且} \quad \sum_{i=1}^N D_i = K $$
解的存在条件
对于给定的 ,解存在的充要条件是:
证明:
- 如果 ,那么即使每个 都取下界,总和也超过 ,无解。
- 如果 ,那么即使每个 都取上界,总和也不够 ,无解。
- 如果 ,我们可以从 开始,逐步增加某些 (不超过 )直到总和等于 。
算法设计
由于 可能很大,我们需要高效地找到最小的 :
-
确定搜索范围:
- 最小可能的 :
- 最大可能的 :由于 , 不会太大(实际上样例中最大为 7766)
- 我们可以从 开始枚举,直到找到解
-
验证函数: 对于每个 ,计算:
- $L_i = \lceil K \cdot (P_i - 0.5 \times 10^{-L}) \rceil$
- $R_i = \lfloor K \cdot (P_i + 0.5 \times 10^{-L}) \rfloor$ 然后检查
-
浮点精度处理: 为了避免浮点误差,我们可以用整数运算:
- 将 转换为整数:
- 那么
- 使用高精度整数或分数运算
具体步骤
-
解析输入:
- 读取每个 字符串,确定小数位数
- 将 转换为整数分子:
-
枚举 :
- 从 开始递增
- 对于每个 :
- 下界:$ \text{lower}_i = \lceil \frac{K \cdot A_i - 5 \times 10^{L-1}}{10^L} \rceil $
- 上界:$ \text{upper}_i = \lfloor \frac{K \cdot A_i + 5 \times 10^{L-1} - 1}{10^L} \rfloor $
- 注意: 必须是非负整数,所以下界不能小于 0
-
检查可行性:
- 如果所有 且 ,则找到解
复杂度分析
- 最坏情况下 可能达到 量级
- 对于 , 是可接受的
- 每个 的验证是
- 总复杂度 ,在数据范围内可行
样例验证
样例 1:
3 0.166667 # 1/6 0.333333 # 1/3 0.500000 # 1/2,枚举 :
- 时:,,
- 总和 ,满足条件
代码实现
import math def main(): import sys input = sys.stdin.read data = input().split() N = int(data[0]) P_str = data[1:1+N] # 确定小数位数 L L = max(len(p.split('.')[1]) if '.' in p else 0 for p in P_str) # 转换为整数分子 A = [] for p in P_str: if '.' in p: integer, decimal = p.split('.') decimal = decimal.ljust(L, '0')[:L] numerator = int(integer + decimal) if integer != '0' else int(decimal) else: numerator = int(p) * (10 ** L) A.append(numerator) K = 1 while True: lower_sum = 0 upper_sum = 0 valid = True for a in A: # 计算下界: ceil((K*a - 5*10^(L-1)) / 10^L) lower = (K * a - 5 * 10**(L-1) + 10**L - 1) // 10**L lower = max(lower, 0) # 非负 # 计算上界: floor((K*a + 5*10^(L-1) - 1) / 10^L) upper = (K * a + 5 * 10**(L-1) - 1) // 10**L if lower > upper: valid = False break lower_sum += lower upper_sum += upper if valid and lower_sum <= K <= upper_sum: print(K) return K += 1 if __name__ == "__main__": main()
优化
实际上,由于 不会太大,直接枚举是可行的。
如果 可能很大,我们可以用中国剩余定理或连分数来加速搜索,但本题数据范围下枚举足够。
总结
本题的关键在于:
- 将四舍五入的比例约束转换为整数范围约束
- 利用求和条件 判断解的存在性
- 通过枚举 找到最小解
这是一个典型的数论+枚举问题,考察对浮点精度和整数约束的理解。
-
- 1
信息
- ID
- 4221
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者