【Codeforces】CF 2013 E

E. Prefix GCD

#数论 #暴力 #贪心

题目描述

Since Mansur is tired of making legends, there will be no legends for this task.

You are given an array of positive integer numbers a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an. The elements of the array can be rearranged in any order. You need to find the smallest possible value of the expression

gcd ⁡ ( a 1 ) + gcd ⁡ ( a 1 , a 2 ) + … + gcd ⁡ ( a 1 , a 2 , … , a n ) , \gcd(a_1) + \gcd(a_1, a_2) + \ldots + \gcd(a_1, a_2, \ldots, a_n), gcd(a1)+gcd(a1,a2)++gcd(a1,a2,,an),
where gcd ⁡ ( a 1 , a 2 , … , a n ) \gcd(a_1, a_2, \ldots, a_n) gcd(a1,a2,,an) denotes the greatest common divisor ( G C D ) (GCD) (GCD) of a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an.

输入格式

Each test contains multiple test cases. The first line contains the number of test cases t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104). The description of the test cases follows.

The first line of each test case contains a single number n n n ( 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105) — the size of the array.

The second line of each test case contains n n n numbers a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an ( 1 ≤ a i ≤ 1 0 5 1 \le a_i \le 10^5 1ai105) — the initial array.

The sum of n n n over all test cases does not exceed 1 0 5 10^5 105.

The sum of max ⁡ ( a 1 , a 2 , … , a n ) \max(a_1, a_2, \ldots, a_n) max(a1,a2,,an) over all test cases does not exceed 1 0 5 10^5 105.

输出格式

For each test case, output a single number on a separate line — the answer to the problem.

样例 #1

样例输入 #1

5
3
4 2 2
2
6 3
3
10 15 6
5
6 42 12 52 20
4
42 154 231 66

样例输出 #1

6
6
9
14
51

解法

解题思路

首先我们发现,前缀 g c d gcd gcd一定是一个不递增的,并且前缀 g c d gcd gcd一定只有 l o g 2 log_2 log2种。由于不递增这个性质,我们可以发现,如果我们第一个数字选择最小的一个数字,并且后面选的数与前面那个数的 g c d gcd gcd也是最小的,那么这样构造的前缀 g c d gcd gcd的和一定是最小的,即 ∑ i = 1 n ∑ j = 1 i g c d ( a i , a j ) \sum_{i=1}^{n} \sum_{j=1}^{i} gcd(a_i,a_j) i=1nj=1igcd(ai,aj)最小。

贪心的正确性显然,因为无论如何都是不递增的,那么直接构造一个前部分递减,后部分全部都相等的序列,一定是 g c d gcd gcd前缀和最小的。

由于只有 l o g 2 log_2 log2 g c d gcd gcd的结果,因此我们如果得到了最小的 g c d gcd gcd结果后,就可以结束了,因为剩下的部分一定都是相等的一段,最后加上那一段的和即可。

代码

void solve() {
	int n;
	std::cin >> n;
	
	std::vector<int>a(n + 1);
	int g = 0;
	for (int i = 1; i <= n; ++i) {
		std::cin >> a[i];
		g = std::gcd(g, a[i]);
	}
 
	auto pos  = std::min_element(a.begin() + 1, a.end());
 
	std::vector<int>vis(n + 1);
	vis[pos - a.begin()] = 1;
 
	int now = *pos, cnt = 1, res = now;
	while (now > g) {
		++cnt;
		pii minx = { inf,inf };
		for (int i = 1; i <= n; ++i) {
			if (vis[i]) continue;
 
			auto& [min, idx] = minx;
			if (std::gcd(now, a[i]) < min) {
				min = std::gcd(now, a[i]);
				idx = i;
			}
		}
 
		auto [min, idx] = minx;
		vis[idx] = 1; now = min;
		res += now;
	}
 
	res += (n - cnt) * g;
 
	std::cout << res << "\n";
	
 
}
 
signed main() {
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	std::cout.tie(0);
 
	int t = 1;
	std::cin >> t;
 
	while (t--) {
		solve();
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值