题目链接

题目大意

给一个长度为 n n n ( 1 ≤ n ≤ 1 0 5 ) (1 \leq n \leq 10^5) (1n105)的数组 a [ i ] a[i] a[i] ( 1 ≤ a i ≤ 1 0 5 ) (1 \leq a_i \leq 10^5) (1ai105),可以对数组任意排序,求 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(16n) .

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;
}
Logo

集算法之大成!助力oier实现梦想!

更多推荐