P8636 [蓝桥杯 2016 省 AB] 最大比例

题目描述

X 星球的某个大奖赛设了 级奖励。每个级别的奖金是一个正整数。

并且,相邻的两个级别间的比例是个固定值。

也就是说:所有级别的奖金数构成了一个等比数列。比如:

其等比值为:。

现在,我们随机调查了一些获奖者的奖金数。

请你据此推算可能的最大的等比值。

输入格式

第一行为数字 ,表示接下的一行包含 个正整数。

第二行 个正整数 ,用空格分开。每个整数表示调查到的某人的奖金数额。

输出格式

一个形如 的分数,要求 、 互质。表示可能的最大比例系数。

测试数据保证了输入格式正确,并且最大比例是存在的。

输入输出样例 #1

输入 #1

3
1250 200 32

输出 #1

25/4

输入输出样例 #2

输入 #2

4
3125 32 32 200

输出 #2

5/2

输入输出样例 #3

输入 #3

3
549755813888 524288 2

输出 #3

4/1

说明/提示

时限 3 秒, 256M。蓝桥杯 2016 年第七届省赛

蓝桥杯 2016 年省赛 A 组 J 题(B 组 J 题)。

解析

解题思路:

这道题要求我们从给定的一部分奖金数额中,推算出可能的最大公比(即最大比例)。

  1. 基本分析: 由题意知,所有奖金构成一个等比数列,公比一定可以表示为最简分数 (因为各项均是正整数,且严格递增)。 将我们抽样调查的数字去重并按升序排列,得到 。 因为它们是从同一个等比数列中抽取出来的,所以相邻两项的比值必然是公比 的某个正整数次幂。 即对于任意的 ,有:

  2. 分子与分母分离: 因为 ,自然也就有 。 所以,如果我们将 约分到最简形式 ,我们就可以确切地得到:

  3. 求最大的公比: 题目要求我们找到“最大的公比”,也就是说我们要让 的值尽可能大。换个角度来看, 越小, 就越大。这就等价于我们要找到所有 的“最大公共底数”作为 ,以及所有 的“最大公共底数”作为 。 由于 ,我们实际上是要求所有 的最大公约数 对应的底数 。这和欧几里得算法求最大公约数非常类似: 对于两个同底数的幂 和 (假设 ),它们的除法 正好对应了指数的减法。当不断进行相除并交换较小数时,直到两数相等,这本质上就是指数在进行“更相减损术”(或者说欧几里得算法)。

  4. 算法流程:

    • 读入所有数字,去重并排序。
    • 分别计算出相邻两项的最简分数比值,收集所有的分子 放入列表,分母 放入列表。
    • 利用自定义的指数级别的最大公约数函数(不断让大数整除小数),求出所有分子的“最大公共底数” 和所有分母的“最大公共底数” 。
    • 最终输出 即可。

代码

//
// Created by 23264 on 2026/2/21.
//
#include<bits/stdc++.h>
using namespace std;
 
#define ll long long
 
ll GCD(ll a,ll b)
{
    while (b > 0)
    {
        ll temp = a % b;
        a = b;
        b = temp;
    }
    return a;
}
 
ll powerGCD(ll x,ll y)
{
    if (x == 0 || y == 0)
    {
        return 0;
    }
    while (true)
    {
        if (x == 1)
        {
            return y;
        }
        if (y == 1)
        {
            return x;
        }
        if (x < y)
        {
            swap(x,y);
        }
        x = x / y;
    }
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n = 0;
    cin >> n;
    vector<ll> arr(n);
    for (int i = 0;i < n;i++)
    {
        cin >> arr[i];
    }
    sort(arr.begin(),arr.end());
    arr.erase(unique(arr.begin(),arr.end()),arr.end());
    // 只有一个唯一元素时,公比直接是 1/1
    if (n <= 1) {
        cout << "1/1";
        return 0;
    }
 
    vector<ll> NList;
    vector<ll> DList;
 
    // 计算相邻项的比例,并化简为最简分数
    for (int i = 0;i < n - 1;i++)
    {
        ll num = arr[i + 1];
        ll den = arr[i];
        ll g = GCD(num,den);
        NList.push_back(num / g);
        DList.push_back(den / g);
    }
 
    // 对所有最简分数的分子求共同的“最大公共底数” A
    ll ansN = NList[0];
    for (int i = 1;i < NList.size();i++)
    {
        ansN = powerGCD(ansN,NList[i]);
    }
 
    // 对所有最简分数的分母求共同的“最大公共底数” B
    ll ansD = DList[0];
    for (int i = 1;i < DList.size();i++)
    {
        ansD = powerGCD(ansD,DList[i]);
    }
 
    cout << ansN << "/" << ansD;
}