HDU 6237 A Simple Stone Game——思维

版权声明:欢迎大家转载,转载请注明出处 https://blog.csdn.net/hao_zong_yin/article/details/83508578

题意:有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;
}

猜你喜欢

转载自blog.csdn.net/hao_zong_yin/article/details/83508578