1 条题解

  • 0
    @ 2025-10-26 23:03:47

    题解:投票人数统计问题

    题目分析

    我们有 NN 个比例 PiP_i,它们四舍五入到小数点后 LL 位。
    我们需要找到最小的正整数 KK,使得存在非负整数 D1,,DND_1, \dots, D_N 满足:

    1. 比例约束

      $$P_i - \frac12 \times 10^{-L} \le \frac{D_i}{K} < P_i + \frac12 \times 10^{-L} $$
    2. 整数约束DiD_i 是非负整数

    3. 总和约束i=1NDi=K\sum_{i=1}^N D_i = K


    数学转换

    将比例约束转换为 DiD_i 的范围:

    $$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$(下界向上取整,因为 DiD_i 是整数)
    • $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 $$

    解的存在条件

    对于给定的 KK,解存在的充要条件是:

    i=1NLiKi=1NRi\sum_{i=1}^N L_i \le K \le \sum_{i=1}^N R_i

    证明

    • 如果 Li>K\sum L_i > K,那么即使每个 DiD_i 都取下界,总和也超过 KK,无解。
    • 如果 Ri<K\sum R_i < K,那么即使每个 DiD_i 都取上界,总和也不够 KK,无解。
    • 如果 LiKRi\sum L_i \le K \le \sum R_i,我们可以从 Di=LiD_i = L_i 开始,逐步增加某些 DiD_i(不超过 RiR_i)直到总和等于 KK

    算法设计

    由于 KK 可能很大,我们需要高效地找到最小的 KK

    1. 确定搜索范围

      • 最小可能的 KKKmin=1K_{\min} = 1
      • 最大可能的 KK:由于 L6L \le 6KK 不会太大(实际上样例中最大为 7766)
      • 我们可以从 K=1K = 1 开始枚举,直到找到解
    2. 验证函数: 对于每个 KK,计算:

      • $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$ 然后检查 LiKRi\sum L_i \le K \le \sum R_i
    3. 浮点精度处理: 为了避免浮点误差,我们可以用整数运算:

      • PiP_i 转换为整数:Pi=分子i10LP_i = \frac{\text{分子}_i}{10^L}
      • 那么 KPi=K分子i10LK \cdot P_i = \frac{K \cdot \text{分子}_i}{10^L}
      • 使用高精度整数或分数运算

    具体步骤

    1. 解析输入

      • 读取每个 PiP_i 字符串,确定小数位数 LL
      • PiP_i 转换为整数分子:Ai=round(Pi×10L)A_i = \text{round}(P_i \times 10^L)
    2. 枚举 KK

      • K=1K = 1 开始递增
      • 对于每个 ii
        • 下界:$ \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 $
        • 注意:DiD_i 必须是非负整数,所以下界不能小于 0
    3. 检查可行性

      • 如果所有 loweriupperi\text{lower}_i \le \text{upper}_iloweriKupperi\sum \text{lower}_i \le K \le \sum \text{upper}_i,则找到解

    复杂度分析

    • 最坏情况下 KK 可能达到 10L10^L 量级
    • 对于 L6L \le 6K106K \le 10^6 是可接受的
    • 每个 KK 的验证是 O(N)O(N)
    • 总复杂度 O(NKmax)O(N \cdot K_{\max}),在数据范围内可行

    样例验证

    样例 1

    3
    0.166667  # 1/6
    0.333333  # 1/3  
    0.500000  # 1/2
    

    L=6L=6,枚举 KK

    • K=6K=6 时:D1[0.5,1.5)[1,1]D_1 \in [0.5, 1.5) \Rightarrow [1,1]D2[1.5,2.5)[2,2]D_2 \in [1.5, 2.5) \Rightarrow [2,2]D3[2.5,3.5)[3,3]D_3 \in [2.5, 3.5) \Rightarrow [3,3]
    • 总和 1+2+3=61+2+3=6,满足条件

    代码实现

    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()
    

    优化

    实际上,由于 KK 不会太大,直接枚举是可行的。
    如果 KK 可能很大,我们可以用中国剩余定理或连分数来加速搜索,但本题数据范围下枚举足够。


    总结

    本题的关键在于:

    1. 将四舍五入的比例约束转换为整数范围约束
    2. 利用求和条件 LiKRi\sum L_i \le K \le \sum R_i 判断解的存在性
    3. 通过枚举 KK 找到最小解

    这是一个典型的数论+枚举问题,考察对浮点精度和整数约束的理解。

    • 1

    信息

    ID
    4221
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者