题目链接: 点击打开链接
题意:首先给你一个n,表示有n个坐标。然后你可以向点和点之间加入点(整数)。让点与点之间的距离都相等。你可以忽略一个两点之间的距离,问最少加多少个点。
题解:首先考虑一种最直观的解法。那就是暴力枚举。第一层for循环去枚举忽略的点,第二层for循环去枚举每个点的距离,第三个for循环去计算用这个距离能往其他两个点里面加多少个点。然后看一眼数据范围是N<1e5。然后GG
场上没想出来。 听了队内dalao讲解之后,感觉自己是个小弱智。
首先我们能发现一个问题。我们应该选择什么大小的距离作为最短距离,换句话说是点和点之间的相等的距离。
思考一下会发现这个最短距离一定是各个距离的公共GCD的最大值。这样才能让加入点的个数最小。
然后我们发现题目没那么简单。因为我们还需要去选择忽略一段距离。假设我们忽略 i 位置的距离。那么我们要计算的是gcd(0 ~~ i-1)和 gcd(i+1 ~~ n-1)的gcd.(n-1 是因为n个点话只有n-1段距离);
但是正常去处理的话还是n2的时间复杂的。所以不难想到用前缀和数组和后缀和数组。来降低时间复杂度。
!!!!特此提醒,数组下标太恶心了!!!!!!
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int a[maxn];
int dis[maxn];
int gcd1[maxn];
int gcd2[maxn];
int main(){
int n;
cin >> n ;
for(int i = 0 ; i < n ; i ++)
scanf("%d",&a[i]);
sort(a,a+n);
for(int i = 0 ; i < n - 1; i ++){
dis[i] = a[i+1] - a[i];
}
// for(int i = 0 ; i < n-1 ; i ++)
// cout << dis[i] << " ";
// cout << endl;
gcd1[0] = dis[0];
for(int i = 1 ; i < n - 1; i ++){
gcd1[i] = gcd(dis[i],gcd1[i-1]);
}
// for(int i = 0 ; i < n-1 ; i ++)
// cout << gcd1[i] << endl;
// cout << endl;
gcd2[n-2] = dis[n-2];
for(int i = n - 3; i >= 0 ; i --){
gcd2[i] = gcd(dis[i],gcd2[i+1]);
}
// for(int i = 0 ; i < n-1 ; i ++)
// cout << gcd2[i] << endl;
// cout << endl;
int pos = 0;
int maxx = gcd2[1];
for(int i = 1 ; i < n-2 ; i ++){
if(maxx <= __gcd(gcd1[i-1],gcd2[i+1])){
pos = i;
maxx = __gcd(gcd1[i-1],gcd2[i+1]);
}
}
if(maxx <= gcd1[n-3]){
pos = n - 2;
maxx = gcd1[n-3];
}
int num = 0;
//cout << maxx << " " << pos << endl;
for(int i = 0 ; i < n - 1; i ++){
if(i == pos)
continue;
/*if(dis[i] == maxx){
continue;
}*/
else{
num += ((dis[i]/maxx) - 1);
}
}
cout << num << endl;
}