1. 程式人生 > >HDU 6237 A Simple Stone Game——思維

HDU 6237 A Simple Stone Game——思維

題意:有n(1e5)堆石子,第i堆石子有a[i](1e5)個,現在可以進行任意次操作,每次操作可以把一個石頭從一堆挪到另一堆,問最少操作幾次可以達成這個條件:存在一個x,使得每堆的石子數量都是x的倍數

思路:首先對石子總數進行質因分解,x一定是這些素因子中的一個,這個是我憑感覺蒙的,事實證明確實是對的。然後列舉素因子x,算每個素因子對應的最小移動次數,我們設b[i]=a[i]%x,那麼我們對b從小到達排序,可以這麼理解,為了實現移動次數最少,我們應該把值小的那些b移走,補充值大的那些b,進一步我們應該將值最小的b補充給值最大的b,這個記錄一個l,r,模擬一下就可以了,要注意的是因為x是素因子,所以移動一定是合法的,這樣每個素因子x對應的最小移動次數我們就算完了,最後在這些值中取一個最小值就可以

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const ll INF = 1e14;
int T, N;
ll a[maxn], p[maxn], b[maxn];
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &N);
        ll sum = 0;
        for (int i = 1; i <= N; i++) scanf("%lld", &a[i]), sum += a[i];
        int cnt = 0;
        for (ll i = 2; i * i <= sum; i++) {
            if (sum % i == 0) {
                p[++cnt] = i;
                while (sum % i == 0) sum /= i;
            }
        }
        if (sum > 1) p[++cnt] = sum;
        ll ans = INF;
        for (int i = 1; i <= cnt; i++) {
            for (int j = 1; j <= N; j++) {
                b[j] = a[j] % p[i];
            }
            sort(b+1, b+1+N);
            int l = 1, r = N;
            ll res = 0;
            while (l < r) {
                if (b[l] == 0) { l++; continue; }
                res += b[l];
                while (b[l] != 0) {
                    if (b[r] + b[l] < p[i]) {
                        b[r] += b[l];
                        b[l] = 0;
                    }
                    else {
                        b[l] -= p[i] - b[r];
                        b[r--] = p[i];
                    }
                }
                l++;
            }
            ans = min(ans, res);
        }
        printf("%lld\n", ans);
    }
    return 0;
}