计蒜客-蒜头君的数轴(gcd)

蒜头君的数轴

今天蒜头君拿到了一个数轴,上边有 nnn 个点,但是蒜头君嫌这根数轴不够优美,想要通过加一些点让它变优美,所谓优美是指考虑相邻两个点的距离,最多只有一对点的距离与其它的不同。

蒜头君想知道,他最少需要加多少个点使这个数轴变优美。

输入格式

输入第一行为一个整数 n(1≤n≤105)n(1 \leq n \leq 10^5)n(1n105),表示数轴上的点数。

第二行为 nnn 个不重复的整数 x1,x2,...,xn(−109≤xi≤109)x_1,x_2,...,x_n(-10^9 \leq x_i \leq 10^9)x1,x2,...,xn(109xi109),表示这些点的坐标,点坐标乱序排列。

输出格式

输出一行,为一个整数,表示蒜头君最少需要加多少个点使这个数轴变优美。

样例输入

4
1 3 7 15

样例输出

1
  1. 主要思路是求:最大公约数。  
  2. n个数,互不相同,则求相邻两点距离,共有n-1个距离。  
  3. 题目允许最多有一对点距离与其他不同,因此对于n-1个距离,我们  
  4. 都要考虑到,因此要对n-1个距离追个删去,对剩余n-2个距离求共同  
  5. 的最大公约数。为了求得这个最大公约数。  
  6.   
  7. 则解法如下:  
  8. 1.如果n<=3,则不需要考虑题中操作就满足题目要求,答案为0.  
  9. 2.除情况1外的其他情况。  
  10.   
  11. 设置gcd1[i]:表示前i个距离求得的公共最大公约数。  
  12. 设置gcd2[i],表示后i个距离求得的公共最大公约数。  
  13. 则考虑逐个删除某个距离,假设当前删除第i个距离。  
  14. i = 1,即删除第一个距离,则需要后n-2个距离的最大公约数,即gcd2[n-2].  
  15. i = n-1,即删除最后一个距离,则需要前n-2个距离的最大公约数,即gcd1[n-2].  
  16. 若i > 1 && i < n-1,则需要前i-1个距离的最大公约数和后n-1-i个距离的最大公约数  
  17. 和在一起求出最大公约数。即gcd(gcd1[i-1],gcd2[n-i-1]). 

那么得到了我们要操作的n-2段距离的最大公因数,那么我们的目标就是通过进行截取(也就是题目说的加点)将这些距离都变成这个最大公因数的长度,那么我们把这n-2个总距离算出来,除以这个最大公因数就得到了要把这一个整体的长度分成都是长度为最大公因数的长度需要多少点,为啥?

因为加入我们每一段长度d1,d2,d3..di,最大公因数g,那么他们的和一定可以被g整除,整除后的结果tmp就是可以分成多少段g的长度,那么加上两边的点,我们需要tmp+1个点进行分割·-·-·-·-·-·-·,注意两边也需要点,那么原来我删去了一段距离,删去这段距离后,两边距离要连起来中间要加一个点,所以删去一个距离相当于删去一个点,所以原来有n-1个点,我们总共需要tmp+1个点,那么我们新需要加的点就是tmp+1-(n-1) = tmp-n+2个

code:

#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int gcd1[maxn];//gcd1[i]是前i个距离的gcd
int gcd2[maxn];//gcd2[i]是后i个距离的gcd
int arr[maxn];
ll dist[maxn];
int gcd(int a,int b){
    if(b == 0) return a;
    else return gcd(b,a%b);
}
int main(){
    int n;
    ll sum;
    scanf("%d",&n);
    for(int i = 0; i < n; i++){
        scanf("%d",&arr[i]);
    }
    if(n <= 3){
        printf("0\n");
        return 0;
    }
    sort(arr,arr+n);
    sum = 0;
    for(int i = 1; i < n; i++){
        dist[i] = arr[i] - arr[i-1];
        sum += dist[i];
    }//储存距离并算出总的距离长度
    int d = dist[0];
    for(int i = 1; i <= n-1; i++){
        d = gcd(d,dist[i]);
        gcd1[i] = d;//记录正向前i个距离的gcd
    }
    d = dist[n];
    for(int i = n-1; i >= 1; i--){
        d = gcd(d,dist[i]);
        gcd2[n-i] = d;
    }
    int Min = 0x3f3f3f3f;
    int temp;
    //枚举删除每一个区间,即这个区间可以不管,其他区间全部变成相同的长度
    for(int i = 1; i < n; i++){
        if(i == 1){
            temp = (sum - dist[i]) / gcd2[n-2];
        }
        else if(i == n-1){
            temp = (sum - dist[i]) / gcd1[n-2];
        }
        else{
            temp = (sum - dist[i]) / gcd(gcd1[i-1],gcd2[n-i-1]);
        }
        Min = min(Min,temp-n+2);
    }
    printf("%d\n",Min);
}

你可能感兴趣的:(思维技巧)