题目链接
题目大意
给一个长度为 n n n ( 1 ≤ n ≤ 1 0 5 ) (1 \leq n \leq 10^5) (1≤n≤105)的数组 a [ i ] a[i] a[i] ( 1 ≤ a i ≤ 1 0 5 ) (1 \leq a_i \leq 10^5) (1≤ai≤105),可以对数组任意排序,求 g c d ( a 1 ) + g c d ( a 1 + a 2 ) + ⋅ ⋅ ⋅ + g c d ( a 1 , a 2 , ⋅ ⋅ ⋅ , a n ) gcd(a_1)+gcd(a_1+a_2)+ \cdot \cdot \cdot + gcd(a_1,a_2, \cdot \cdot \cdot ,a_n) gcd(a1)+gcd(a1+a2)+⋅⋅⋅+gcd(a1,a2,⋅⋅⋅,an) 的最小值。
思路
首先在不断加入数的过程中, g c d gcd gcd 要么保持不变要么会下降,且最少会下降 2 2 2。考虑 g c d gcd gcd 的种类, l o g ( 1 0 5 ) / l o g ( 2 ) < 17 log(10^5)/log(2)<17 log(105)/log(2)<17 ,在这题也就最多16个。暴力循环最多16次找到使得 g c d gcd gcd下降最猛的数放前面,直到没有数使得 g c d gcd gcd 下降,剩下的所有数的前缀 g c d gcd gcd 值将保持为同一个最小的值,时间复杂度 O ( 16 ∗ n ) O(16*n) O(16∗n) .
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
bool st[N];
int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
void solve() {
int n;
cin>>n;
memset(st,0,sizeof st);
for(int i=1;i<=n;++i)
cin>>a[i];
sort(a+1,a+n+1);
int res=a[1];
int now=a[1];
st[1]=1;
int cnt=1;
while(1) {
int flag=0;
int tmp=now;
for(int i=1;i<=n;++i) {
if(st[i]) continue;
if(gcd(a[i],now)<tmp) {
tmp=gcd(a[i],now);
flag=i;
}
}
if(!flag) break;
else {
cnt++;
st[flag]=1;
now=tmp;
res+=now;
}
}
// cout<<cnt<<' '<<now<<'\n';
res+=(n-cnt)*now;
cout<<res<<'\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--) solve();
return 0;
}