1 条题解

  • 0
    @ 2026-5-14 18:36:44

    题目解析

    给定区间 [l,r][l, r] 和目标最大公约数 GG,需要找到两个数 AABBlABrl \le A \le B \le r),使得 gcd(A,B)=G\gcd(A, B) = G,并且 AB|A - B| 尽可能大。如果有多个解,选择 AA 最小的。


    关键观察

    1. 化简问题

    如果 gcd(A,B)=G\gcd(A, B) = G,那么可以设 A=GaA = G \cdot aB=GbB = G \cdot b,其中 gcd(a,b)=1\gcd(a, b) = 1
    此时 aabb 的范围为:

    $$\left\lceil \frac{l}{G} \right\rceil \le a \le b \le \left\lfloor \frac{r}{G} \right\rfloor $$

    原问题转化为:在区间 [L,R][L, R] 内找两个互质的数 a,ba, bL=l/GL = \lceil l/G \rceilR=r/GR = \lfloor r/G \rfloor),使得 bab - a 最大,且 aa 尽量小。

    2. 贪心策略

    为了最大化距离,我们想让 aa 尽量小,bb 尽量大,即尝试 a=La = Lb=Rb = R

    • 如果 gcd(L,R)=1\gcd(L, R) = 1,则 (A,B)=(LG,RG)(A, B) = (L \cdot G, R \cdot G) 即为答案。
    • 否则,需要稍微调整 aabb,使得它们互质。由于只相差一点点,距离几乎最大。

    3. 调整范围

    从最大距离开始,依次尝试距离减 11、减 22 等。
    对于距离 d=RLkd = R - L - k,可能的数对为:

    $$(L + i, R - (k - i)) \quad \text{for } i = 0,1,\dots,k $$

    即固定 aabb 的和为 L+RkL + R - k,差值最大为 RLkR - L - k

    因为 L,RL, R 可能很大(101810^{18} 级别),不能枚举所有 kk。但可以证明,kk 只需要尝试到大约 6060 即可,因为当区间长度足够时,一定能找到互质对。

    4. 为什么 kk 很小

    在区间 [L,L+30][L, L+30][R30,R][R-30, R] 中,一定存在一对互质的数(通过容斥原理可证)。
    因此,最多需要尝试 6060 次距离递减(k60k \le 60),每次检查最多 k+1k+1 个数对,总复杂度 O(602log)O(60^2 \cdot \log) 可接受。


    算法步骤

    1. 读入 l,r,Gl, r, G
    2. 计算 L=l/GL = \lceil l / G \rceilR=r/GR = \lfloor r / G \rfloor
      L>RL > R,则无解。
    3. k=0k = 0 开始,枚举距离减小量:
      • 尝试所有 i=0ki = 0 \dots k,设 a=L+ia = L + ib=R(ki)b = R - (k - i)
      • 检查是否 LabRL \le a \le b \le R,且 gcd(a,b)=1\gcd(a, b) = 1
      • 如果找到,输出 A=aGA = a \cdot GB=bGB = b \cdot G
    4. 如果尝试完 k=0k = 0 到某个上限(如 6060)仍未找到,输出 -1 -1

    时间复杂度

    每个测试用例检查 O(602)=3600O(60^2) = 3600 个数对,每次检查 gcd\gcdO(log)O(\log),总复杂度 O(t3600log)O(t \cdot 3600 \cdot \log)t1000t \le 1000 可接受。


    C++ 代码实现

    #include <bits/stdc++.h>
    using namespace std;
    
    using ll = long long;
    
    ll gcd(ll a, ll b) {
        while (b) {
            a %= b;
            swap(a, b);
        }
        return a;
    }
    
    void solve() {
        ll l, r, g;
        cin >> l >> r >> g;
        
        // 找到区间内第一个和最后一个能被 g 整除的数
        ll L = (l + g - 1) / g;  // ceil(l / g)
        ll R = r / g;            // floor(r / g)
        
        if (L > R) {
            cout << "-1 -1\n";
            return;
        }
        
        // 尝试距离从大到小
        for (ll k = 0; k <= 60; k++) {
            // 距离 = R - L - k
            for (ll i = 0; i <= k; i++) {
                ll a = L + i;
                ll b = R - (k - i);
                if (a > b) continue;
                if (a < L || b > R) continue;
                if (gcd(a, b) == 1) {
                    cout << a * g << " " << b * g << "\n";
                    return;
                }
            }
        }
        
        cout << "-1 -1\n";
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        
        int t;
        cin >> t;
        while (t--) {
            solve();
        }
        return 0;
    }
    

    验证样例

    输入:

    4
    4 8 2
    4 8 3
    4 8 4
    5 7 6
    

    输出:

    4 6
    -1 -1
    4 8
    6 6
    

    与题目输出一致。


    补充说明

    • 代码中 kk 的上限设为 6060 是安全的,实际所需往往更小。
    • 注意使用 long long 避免溢出。
    • GG 很大时,LLRR 可能很小,但算法依然正确。
    • 1

    信息

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