2018 蓝桥杯省赛 A 组模拟赛 蒜头君的数轴

题目链接: 点击打开链接

题意:首先给你一个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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值