HDU6237

​ 这是2017年ccpc哈尔滨站的一道数论题,很荣幸在我们学校举办,题目另辟蹊径,很好的一道素数题。

题意:

Problem Description

After he has learned how to play Nim game, Bob begins to try another stone game which seems much easier.

The game goes like this: one player starts the game with N piles of stones. There is ai stones on the ith pile. On one turn, the player can move exactly one stone from one pile to another pile. After one turn, if there exits a number x(x>1) such that for each pile bi is the multiple of x where bi is the number of stone of the this pile now), the game will stop. Now you need to help Bob to calculate the minimum turns he need to stop this boring game. You can regard that 0 is the multiple of any positive number.

Input

The first line is the number of test cases. For each test case, the first line contains one positive number N(1≤N≤100000), indicating the number of piles of stones.

The second line contains N positive number, the ith number ai(1≤ai≤100000) indicating the number of stones of the ith pile.

The sum of N of all test cases is not exceed 5∗105.

Output

For each test case, output a integer donating the answer as described above. If there exist a satisfied number x initially, you just need to output 0. It’s guaranteed that there exists at least one solution.

Sample Input

2
5
1 2 3 4 5
2
5 7

Sample Output

2
1

​ 翻译成汉语,大概意思就是给你一个T代表有T组样例,然后每组样例第一行给出一个n代表有n堆石子,然后分别输入每堆石子的石子数。只能从一堆石子中取一个石子移动到另一堆,问至少移动几次能使得每一堆的石子数的公共gcd > 1(若此时已经gcd>1了就输出0,一堆石子允许全部移光)

思路:

​ 我们求出所有堆石子的总和sum,然后筛选出它的全部素因子。也就是说,要想让每堆石子的公共gcd大于1,至少其中一个素因子是全部石子堆的因子。因此我们需要遍历每个素因子,分别求出每堆石子数变成该素因子的倍数所需的最小步数,取个最小值就是答案

代码:

#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <algorithm>
#define d int32_t
#define ll int64_t
#define r return
#define N 100000
#define For(i, star, endd) for(d i = star; i <= endd; i++)
#define mem(a) memset(a, 0, sizeof(a))
using namespace std;

d T, n;
d a[N + 5];
d b[N + 5];
ll prime[105];
bool book[N + 5];
ll prime1[N + 5];

//筛1e5以内素数
void init() {
    mem(book);
    prime1[0] = 0;
    For(i, 2, N) {
        if (!book[i]) {
            prime1[++prime1[0]] = i;
        }
        For(j, 1, prime1[0]) {
            ll t = prime1[j] * i;
            if (t > N) break;
            book[t] = 1;
            if (i % prime1[j] == 0) break;
        }
    }
    r;
}

//筛选出t的全部素因子
void su(ll t) {
    prime[0] = 0;
    for (d i = 1; i <= prime1[0]; i ++) {
        if (t % prime1[i] == 0) {
            prime[++prime[0]] = prime1[i];
            while (t % prime1[i] == 0) {
                t /= prime1[i];
            }
            if (t == 1) break;
        }
    }
    if (t != 1) {
        prime[++prime[0]] = t;
    }
    r;
}

//从大到小排列
bool cmp (d A, d B) {
    r A > B;
}

//求每堆素因子为t的倍数时的最小移动次数
ll Num(ll t) {
    ll sum = 0, ans = 0;
    For(i, 1, n) {
        b[i] = a[i] % t;
        sum += b[i];
    }
    sort(b + 1, b + n + 1, cmp);
    For(i, 1, n) {
        if (b[i] == 0) break;
        ans += t - b[i];
        sum -= t;
        if (sum == 0) {
            break;
        }
    }
    r ans;
}

//遍历全部素因子,取最小次数
ll work() {
    ll minn = 99999999999;
    For(i, 1, prime[0]) {
        minn = min(minn, Num(prime[i]));
    }
    r minn;
}

d main() {
    init();
    scanf("%d", &T);
    while (T--) {
        ll sum = 0;
        scanf("%d", &n);
        For(i, 1, n) {
            scanf("%d", &a[i]);
            sum += a[i];
        }
        su(sum);
        ll ans = work();
        printf("%lld\n", ans);
    }
    r 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

猜你喜欢

转载自blog.csdn.net/Ivan_zcy/article/details/83114367
hdu