1 条题解

  • 0
    @ 2025-10-25 10:06:17

    「POI2014 R3」太阳能板 Solar Panels 题解

    题目大意

    nn 块太阳能板,每块板的尺寸范围是:

    • 宽度:[smin,smax][s_{\min}, s_{\max}]
    • 高度:[wmin,wmax][w_{\min}, w_{\max}]

    太阳能板由相同大小的正方形电池组成。我们需要为每块板找到最大的电池边长 kk,使得:

    • 存在某个宽度 s[smin,smax]s \in [s_{\min}, s_{\max}]
    • 存在某个高度 w[wmin,wmax]w \in [w_{\min}, w_{\max}]
    • kk 能整除 ssww(即 ssww 都是 kk 的倍数)

    问题分析

    数学形式化

    对于每个查询 (smin,smax,wmin,wmax)(s_{\min}, s_{\max}, w_{\min}, w_{\max}),我们需要找到最大的整数 kk,使得:

    $$\exists s \in [s_{\min}, s_{\max}], \exists w \in [w_{\min}, w_{\max}] \text{ 满足 } k \mid s \text{ 且 } k \mid w $$

    等价于:

    $$\exists s \in [s_{\min}, s_{\max}] \text{ 满足 } k \mid s \quad \text{且} \quad \exists w \in [w_{\min}, w_{\max}] \text{ 满足 } k \mid w $$

    关键观察

    1. 区间整除条件:对于区间 [a,b][a, b] 和除数 kk,存在 x[a,b]x \in [a, b]kk 整除当且仅当:

      $$\left\lfloor \frac{b}{k} \right\rfloor - \left\lfloor \frac{a-1}{k} \right\rfloor \geq 1 $$

      即区间 [a,b][a, b] 内至少包含一个 kk 的倍数。

    2. 最大可能的 kk:显然 kmin(smax,wmax)k \leq \min(s_{\max}, w_{\max})

    算法思路

    方法一:直接枚举(适用于小数据)

    对于 n10n \leq 10 且数据范围较小的情况,可以直接枚举可能的 kk

    int solve(int smin, int smax, int wmin, int wmax) {
        int ans = 1;
        int maxk = min(smax, wmax);
        
        for (int k = maxk; k >= 1; k--) {
            // 检查宽度区间是否有k的倍数
            bool width_ok = (smax / k - (smin - 1) / k) >= 1;
            // 检查高度区间是否有k的倍数  
            bool height_ok = (wmax / k - (wmin - 1) / k) >= 1;
            
            if (width_ok && height_ok) {
                ans = k;
                break;
            }
        }
        return ans;
    }
    

    复杂度O(nmin(smax,wmax))O(n \cdot \min(s_{\max}, w_{\max})),对于大数据不可行。

    方法二:数学优化

    观察:我们只需要检查所有可能的候选 kk,这些候选是:

    • 所有 min(smax,wmax)\leq \min(s_{\max}, w_{\max}) 的整数
    • 但可以跳过一些明显不可能的值

    更聪明的方法:从 min(smax,wmax)\min(s_{\max}, w_{\max}) 开始向下枚举,但利用整除性质提前终止。

    方法三:高效算法

    关键思路:最大可能的 kk 一定是某个区间端点的约数,或者是某些特定值。

    算法步骤

    1. M=min(smax,wmax)M = \min(s_{\max}, w_{\max})
    2. k=Mk = M 开始向下检查
    3. 对于每个 kk,检查:
      • 宽度区间 [smin,smax][s_{\min}, s_{\max}] 是否包含 kk 的倍数
      • 高度区间 [wmin,wmax][w_{\min}, w_{\max}] 是否包含 kk 的倍数
    4. 找到第一个满足条件的 kk 就是答案

    优化:使用整除分块的思想,跳过一些不可能的值。

    代码实现

    #include <bits/stdc++.h>
    using namespace std;
    
    // 检查区间[a, b]中是否存在k的倍数
    bool has_multiple(int a, int b, int k) {
        return (b / k) > ((a - 1) / k);
    }
    
    int solve(int smin, int smax, int wmin, int wmax) {
        int ans = 1;
        int maxk = min(smax, wmax);
        
        // 从大到小枚举k
        for (int k = maxk; k >= 1; k--) {
            // 优化:如果当前答案已经大于等于k,直接返回
            if (ans >= k) break;
            
            if (has_multiple(smin, smax, k) && has_multiple(wmin, wmax, k)) {
                ans = k;
                break;
            }
        }
        return ans;
    }
    
    // 更高效的版本
    int solve_fast(int smin, int smax, int wmin, int wmax) {
        int ans = 1;
        int maxk = min(smax, wmax);
        
        // 使用一些启发式优化
        for (int k = maxk; k >= 1; k--) {
            // 快速检查:如果k已经小于等于当前答案,没必要继续
            if (k <= ans) break;
            
            // 检查宽度区间
            if (smax / k == (smin - 1) / k) continue;
            
            // 检查高度区间
            if (wmax / k == (wmin - 1) / k) continue;
            
            ans = k;
            break;
        }
        return ans;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        
        int n;
        cin >> n;
        
        for (int i = 0; i < n; i++) {
            int smin, smax, wmin, wmax;
            cin >> smin >> smax >> wmin >> wmax;
            
            int result = solve_fast(smin, smax, wmin, wmax);
            cout << result << "\n";
        }
        
        return 0;
    }
    

    复杂度分析

    最坏情况

    • 每次查询需要检查最多 min(smax,wmax)\min(s_{\max}, w_{\max}) 个值
    • 总复杂度:O(nmin(smax,wmax))O(n \cdot \min(s_{\max}, w_{\max}))

    实际性能

    • 由于从大到小枚举,通常很快找到答案
    • 对于随机数据,期望检查次数远小于 min(smax,wmax)\min(s_{\max}, w_{\max})
    • 可以通过额外优化进一步加速

    进一步优化

    优化一:利用区间性质

    如果 smaxsminks_{\max} - s_{\min} \geq k,那么区间 [smin,smax][s_{\min}, s_{\max}] 一定包含 kk 的倍数。

    优化二:只检查候选值

    最大可能的 kk 一定是:

    • smaxs_{\max} 的约数
    • wmaxw_{\max} 的约数
    • 或者是某些边界值

    优化三:二分答案

    虽然答案不满足单调性(大的k满足时小的k一定满足,但反过来不成立),但可以结合其他技巧。

    样例分析

    样例13 9 8 8

    • 最大可能 k=min(9,8)=8k = \min(9, 8) = 8
    • 检查 k=8k=8:宽度区间 [3,9][3,9]88,高度区间 [8,8][8,8]88
    • 答案:88

    样例21 10 11 15

    • 最大可能 k=min(10,15)=10k = \min(10, 15) = 10
    • k=10k=10:宽度区间有 1010,但高度区间 [11,15][11,15] 没有 1010 的倍数
    • k=9k=9:宽度区间有 99,高度区间没有 99 的倍数
    • ...
    • k=7k=7:宽度区间有 77,高度区间有 1414
    • 答案:77

    总结

    这道题的核心在于:

    1. 理解整除在区间中的存在性条件
    2. 从大到小枚举的贪心策略
    3. 利用整数除法的性质进行快速检查
    • 1

    信息

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