2 条题解

  • 2
    @ 2023-11-27 21:51:07

    此题重点在于题干说的“原数列尽可能的短”。

    为了使还原后的原等差数列最短,首先我们不能再添加比给定数列的最大值更大,或比最小值更小的数,因为这样的数对还原等差数列无必要性反而会使数列更长。

    因此,我们添加的数 xx 一定满足 minxmaxmin \leq x \leq maxmaxmaxminmin 分别表示给定数列最大最小值)

    设给定数列长度为 nn排序后数列为 a1,a2a_1,a_2 ...... ana_n
    数列中相邻数之差为 b1,b2b_1,b_2 ...... bn1b_{n-1} (bi=ai+1ai)( b_i=a_{i+1}-a_i)
    因为还原的数列是等差数列,问题可转换为找到一个公差 dddd 必须满足以下条件才可正确还原:

    1. 对于所有的 bib_i ,一定是 dd倍数。即 \forall bib_ibib_i % d=0d = 0
    2. 在满足条件1的前提下, dd 尽可能的大。(为了使原数列尽可能的短)

    条件1也可表述为 dd 一定是所有 bib_i 的公因数,条件2指 dd 应尽可能的大,故可得 dd 为所有 bib_i 的最大公因数。

    因此,我们可以使用讲过的 gcdgcd 算法快速求出所有 bib_i 的最大公因数,即公差 dd 。由等差数列求项数公式 n=ana1d+1n={{a_n-a_1}\over d}+1 求出还原后数列的长度,再使用求和公式 (a1+an)×n2{(a_1+a_n)\times n}\over 2 即可得到原数列和。

    Code:

    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    
    long long gcd(long long x, long long y)// gcd函数求最大公约数
    {
    	long long z;
    	while(y > 0)
    	{
    		z = x % y;
    		x = y;
    		y = z;
    	}
    	return x;
    }
    
    long long a[100010];// 数组定义在主函数外
    int main()
    {
    	int n;
    	scanf("%d", &n);
    	for(int i = 0; i < n; i++)
    	{
    		scanf("%lld", &a[i]);
    	}
    	sort(a, a + n);// 排序a数组
    	if(a[0] == a[n - 1])// 特判全相等的情况
    	{
    		printf("%lld", a[0] * n);
    	}
    	else
    	{
    		long long d = a[1] - a[0];
    		for(int i = 2; i < n; i++)
    		{
    			d = gcd(d, a[i] - a[i - 1]);// 循环计算最大公约数
    		}
    		long long tn = (a[n - 1] - a[0]) / d + 1;// 计算序列项数
    		printf("%lld", (a[0] + a[n - 1]) * tn / 2);// 求和
    	}
    	return 0;
    }
    

    常见错误原因:

    1. 未正确使用gcd
    2. 未特判全相等的特例
    3. 未开 long long 导致超出范围
    • @ 2023-11-28 18:23:33

      tql ❤️ orz

信息

ID
954
时间
1000ms
内存
256MiB
难度
9
标签
递交数
404
已通过
27
上传者