计蒜客 蒜头君的数轴 (gcd前缀和)

传送门


思路:

    首先考虑到要满足除了一对点的距离,其余的相邻两点间的距离要相等,那么最后这个距离r一定要是原本相邻点距离的公约数,且一定是最大公约数才最优.

   n个点n-1个距离,每次去掉一个, 那么如何求每次去掉一个剩下n-2个距离的gcd呢?

由于gcd的非递增性质,也就是 a,b,c三个数的gcd = __gcd(__gcd(a,b),c).我们可以正向维护一个lgcd前缀,逆向维护一个rgcd前缀,当去掉某个距离i时,此时剩下距离的gcd为 

gcd = __gcd(lgcd[i-1],rgcd[i+1])。

注意处理两头的情况

#include <bits/stdc++.h>
#define pb push_back
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int maxn = 1e5+5;
int x[maxn],n;
int dis[maxn];
int lgcd[maxn],rgcd[maxn];
ll sum[maxn];
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i = 1;i <= n;++i)
			scanf("%d",&x[i]);
		if(n <= 3)
		{
			puts("0");
			continue;
		}
		sort(x + 1,x + 1 + n);
		sum[0] = 0;
		for(int i = 1;i < n;++i)
		{
			dis[i] = x[i + 1] - x[i];
			sum[i] = sum[i - 1] + dis[i];
		}
		lgcd[1] = dis[1],rgcd[n - 1] = dis[n - 1];
		for(int i = 2;i < n;++i)
		lgcd[i] = __gcd(lgcd[i - 1],dis[i]);
		for(int i = n - 2;i >= 1;--i)
		rgcd[i] = __gcd(rgcd[i + 1],dis[i]);
		int mi = inf;
		for(int i = 1;i < n;++i)
		{
			int ox = inf;
			if(i == 1)
			{
				ox = (sum[n - 1] - dis[i]) /rgcd[2];
			}
			else if(i == n - 1)
			{
				ox = sum[n - 2]/lgcd[n - 2];
			}
			else
			{
				int gcd = __gcd(lgcd[i-1],rgcd[i + 1]);
				ox = (sum[n - 1] - dis[i]) / gcd;
			}
			mi = min(mi,ox - (n - 2));
		}
		printf("%d\n",mi);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/howardemily/article/details/79763480
今日推荐