1 条题解

  • 0
    @ 2026-5-3 20:14:17

    F. Multicolored Markers 题解


    一、题目回顾

    aa 个红色格子和 bb 个蓝色格子,总共 a+ba+b 个格子。要求:

    1. 所有格子拼成一个矩形(面积为 a+ba+b)。
    2. 至少一种颜色的格子自身也拼成一个矩形(即该颜色的所有格子形成一个子矩形)。

    在所有满足条件的方案中,求该矩形的最小周长

    数据范围:1a,b10141 \le a, b \le 10^{14}


    二、问题分析

    设总矩形面积为 S=a+bS = a + b,边长为 W×HW \times HWHW \le H),周长为 2(W+H)2(W+H)

    不妨假设红色格子形成矩形(另一种情况是对称的,最后对 a,ba,b 互换取最小值即可)。设红色矩形的边长为 w×hw \times hwhw \le h),则一定有:

    • wh=aw \cdot h = a
    • 红色矩形可以放入总矩形中:wWw \le WhHh \le H(红色矩形可以旋转,因此也可以是 hWh \le WwHw \le H,但因为我们要求 whw \le hWHW \le H,所以只需考虑 wWw \le WhHh \le H

    对于固定的总矩形 W×HW \times H,我们希望知道是否存在红色矩形的分解 w×h=aw \times h = a 满足 wWw \le WhHh \le H


    三、算法流程

    3.1 枚举总矩形

    枚举 WW11S\lfloor\sqrt{S}\rfloor,当 SmodW=0S \bmod W = 0 时,取 H=S/WH = S / W,得到一组总矩形分解。

    3.2 判断红色矩形是否可放入

    对于给定的 WW,我们需要判断是否存在 waw \mid a 使得 wWw \le Wa/wHa/w \le H

    等价于:找到一个 ww 使得 wWw \le Wwa/Hw \ge a / H(由 a/wHa/w \le H 推出 wa/Hw \ge a/H)。因此我们需要在 aa 的因子中,找一个落在区间 [a/H,W][\lceil a/H \rceil, W] 内的因子。

    实际上,因为 H=S/WH = S/WWW 增大而减小,条件 a/wHa/w \le H 随着 WW 增大变得更容易满足(HH 变小会收紧条件?仔细看:WW 增大 → HH 减小 → 要求 a/wHa/w \le H 更严格,所以其实更大的 WW 对红色矩形的宽度约束更紧)。我们需要在 WW 增长的过程中维护可行的 ww

    关键观察:对于固定的 WW,我们只需要考虑最大的满足 wWw \le Waa 的因子。因为当 ww 越大,h=a/wh = a/w 越小,条件 hHh \le H 越容易满足。因此检查那个最大的 ww 即可。

    3.3 双指针快速判定

    预处理出 aa 的所有因子(只存 a\le \sqrt{a} 的部分),排序为数组 d[0k1]d[0 \dots k-1]

    枚举 WW 递增,用指针 pp 指向当前的最大因子满足 d[p]Wd[p] \le W。因为 WW 单增,指针也可以单增。

    对每个合法的 WW,检查 a/d[p]Ha / d[p] \le H。若满足,则用周长 2(W+H)2(W+H) 更新答案。

    3.4 对称处理

    以上假设红色形成矩形。蓝色形成矩形的情况只需交换 a,ba,b 再跑一遍,取最小周长。


    四、复杂度分析

    • 预处理因子:O(max(a,b))O(\sqrt{\max(a,b)})
    • 枚举总矩形边长:O(S)O(\sqrt{S})
    • 双指针均摊 O(因子个数)O(\text{因子个数}),小于 O(a)O(\sqrt{a})

    总时间复杂度 O(a+b+a+b)O(\sqrt{a} + \sqrt{b} + \sqrt{a+b}),在 a,b1014a,b \le 10^{14} 时可轻松通过。


    五、参考代码

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long li;
    
    const int N = 1000000; // 1e6, 足够存放 sqrt(1e14) 内的因子
    int lens[N];
    int k;
    
    // 假设颜色 a 形成矩形,b 为另一种颜色
    li solve(li a, li b) {
        k = 0;
        // 枚举 a 的因子(只存 ≤√a 的部分)
        for (li i = 1; i * i <= a; ++i)
            if (a % i == 0)
                lens[k++] = i;
    
        li ans = 2 * (a + b) + 2;   // 初始化为一个极大的周长
        li S = a + b;
        int p = 0;
    
        // 枚举总矩形的一边 W
        for (li W = 1; W * W <= S; ++W) {
            if (S % W != 0) continue;
            li H = S / W;
    
            // 移动指针到满足 lens[p] <= W 的最大因子
            while (p + 1 < k && lens[p + 1] <= W)
                ++p;
    
            // 检查另一边是否也放得下
            if (a / lens[p] <= H)
                ans = min(ans, 2 * (W + H));
        }
        return ans;
    }
    
    int main() {
        li a, b;
        scanf("%lld%lld", &a, &b);
        // 分别假设红色或蓝色形成矩形
        printf("%lld\n", min(solve(a, b), solve(b, a)));
        return 0;
    }
    

    • 1

    信息

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