【算法】【最大公因数入门】

2 阅读1分钟

codeforces.com/gym/105930/…

#include <bits/stdc++.h>  
using namespace std;  
using ll=long long;  
#define int long long  
vector<int>dis(int x){  
    vector<int>b;  
    for(int i=1;i*i<=x;i++){  
        if(x%i==0){  
            b.push_back(i);  
            if(i*i!=x)b.push_back(x/i);  
        }  
    }return b;  
}  
void so(){  
      
}signed main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    int t;cin>>t;  
    while(t--){  
        int n,k;cin>>n>>k;  
        vector<int>a(n+1);  
        int ma=0;  
        int suma=0;  
        for(int i=1;i<=n;i++){  
            cin>>a[i];  
            suma+=a[i];  
            ma=max(ma,a[i]);  
        }vector<int>fre(ma+1,0);  
        vector<int>sum(ma+1,0),pc(ma+1,0);  
        for(int i=1;i<=n;i++)fre[a[i]]++;  
        for(int i=1;i<=ma;i++){  
            pc[i]=pc[i-1]+fre[i];  
            sum[i]=sum[i-1]+fre[i]*i;  
        }// 目标:找最大的d,使得把所有元素变成d的倍数,所需的最少操作数 ≤ k  
        // 所有可能的d必须是 (suma + k) 的约数!  
        // 原理:操作后总和 = suma + k,而所有元素都是d的倍数 → 总和是d的倍数 → d | (suma + k)  
        int ta=suma+k;  
        auto v=dis(ta);  
        sort(v.begin(),v.end(),greater<int>());  
        for(auto x:v){  
            int res=0;  
            if(x>ma){  
                res+=n*x-suma;//所有都变成x  
            }//d<=ma  
            //把所有数变成>=自己的d得倍数  
            //每个数 a_i 必须变成 ≥ a_i 的最小 x 的倍数  
            //比如  
            //x=5  
            //a_i=2 → 变成 5(加 3)  
            //a_i=8 → 变成 10(加 2)  
            //a_i=9 → 变成 10(加 1)  
            else{  
                for(int t=1;(t-1)*x<=ma;t++){  
                    int l=(t-1)*x+1;  
                    int r=min(ma,t*x);  
                    res+=t*(pc[r]-pc[l-1])*x-(sum[r]-sum[l-1]);  
                    if(res>k)break;  
                }  
            }//最少操作次<=k,且剩余操作次数(k-res)是x的倍数  
            if((k-res)%x==0&&k-res>=0){cout<<x<<'\n';break;}  
        }  
        }  
}

思路

1.把每个数映射到桶,到前缀和 2.公式变形,枚举因数