前前天写了cf的比赛题,很不幸被虐的很惨,再次后来慢慢补提,现在感觉这个题不错写一下自己的心得。
这个图片是题
大致意思就是有T组数据,每组数据有n个数,输入n和k,现在你可以选择一个数x,把x删去,并在找到两个数y和z,并且要满足y+z == x+k(双等,突出严谨),把它们添到数组中,问最后能不能把这个数组里的数最终变为全部相等,如果不能的话输出-1,能的话输出最小的次数。OK,题意介绍完毕,下面开始分析:
题干说要选择一个数加上k后删去变成两个数,即ai+k=y+z,我们不妨设每个 ai 经过 ri 次变化后变成最终的数 ans,这里可能会有一个疑惑,ai + k不是变成另外两个数y,z没有了吗?ai 在这个数组中确实没有了,但它留下来的影响还在,即有这样一个关系 ai + k = y + z,如果y和z不相等的话,之后我们在后续的操作中会对 y 和 z , 进行一样的的操作,比如说y + k = m + n,这样是不是相当于,在 ai那边再加了一个k,变成 ai + 2k = m + n + z,如此不断操作下去,直到右边的数全部相等,根据上边假设我们操作了 ri 次,我们最终会得到一个关系式 ai + ri * k = (ri+1)*ans,然后我们就可以愉快的化简,ri = (ai - ans) / (ans-k),然后现在我们得到这个式子,乍一看啥都看不出来,因为上下都有未知量ans,所以我们就可以进行分式,把上边变成(ai - k - ans +k),然后进行变换,动手划两下,可以变成 ri = (ai - k)/(ans-k) - 1,然后就来到重点了,我们是不是要求 ri 尽可能小,所以 ans - k是不是要尽可能地大?ans - k 是不是要对每一个 ai - k 进行相除,所以ans - k 要做到:1.能被每个 ai -k 除尽;2.它要尽可能大,所以我们能联想到什么?是不是所有ai - k的最大公因数?然后再对所有 ri 相加即可。OK,想到这里你已经了解这道题的主要轮廓了,下面介绍一些细节:
上面我们是不是只讨论了存在最小值的情况,但题中还说如果做不到的话输出 -1,所以我们还需要判断有没有不能化成全部相等的情况。来让我们回过头观察ai +k == y + z,y和z如果存在的话,来让我们感性理解一下,最终不论哪个 ai 我们右边是不是换成了一个相等的数,所以在这个过程中能不能存在一些ai大于k,一些ai小于k?肯定不行吧,这会导致右边的数变得不相等,所以要么全部的 ai 大于k,要么全部的 ai 小于k,即如果排序后有(a[1]<=k&&a[n]>k) II (a[1]<k&&a[n]>=k),那么就可以输出-1了。还有一种情况,ai = k,此时分母为零,我们要单独判断,还有一样情况 ai 全部相等,这两种情况可以放在一块判断,即排序后a[1]==a[n],输出0。
下面贴上代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
long long n, k, t;
long long a[200010];
long long gcd(long long x, long long y)
{
while (y)
{
long long c = x % y;
x = y;
y = c;
}
return x;
}
void solve()
{
cin >> n >> k;
memset(a, 0, sizeof a);//及时清理a[i]
long long ans = 0;
long long g = 0;
long long sum = 0;//防止污染后边的sum!!!
for (int i = 1; i <= n; i++)
{
cin >> a[i];
g = gcd(g, a[i] - k);//自己手搓一个gcd
}
sort(a + 1, a + 1 + n);
if (a[1] == a[n])
{
cout << 0 << endl;
return;
}
if ((a[1]<=k && a[n]>k)||(a[1]<k && a[n]>=k))
{
cout << -1 << endl;
return;
}
for (int i = 1; i <= n; i++)
{
ans += ((a[i] - k) / g - 1);
}
cout << ans << endl;
return;
}
int main()
{
cin >> t;
while (t--)
{
solve();
}
return 0;
}
这是蒟蒻对这道题的浅薄看法