CCPC.2017 哈尔滨站-H - A Simple Stone Game HDU - 6237

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 NN piles of stones. There is aiai stones on the iith 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)x(x>1) such that for each pile bibi is the multiple of xx where bibi 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 00 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)N(1≤N≤100000), indicating the number of piles of stones. 

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


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

Output

For each test case, output a integer donating the answer as described above. If there exist a satisfied number xx initially, you just need to output 00. 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

题意就是给你N堆石头 每次可以将一块石头从一堆移动到另一堆,求最小的移动次数使每一堆都是某个数 X的倍数

理解:每一堆都是X的倍数那么石头的总数必然是X的倍数,那么就可以枚举石头总数的质因子,求出符合条件的最小次数便可以

枚举质因子可以用欧拉筛打表预处理一下素数表,然后枚举素数,如果不能整除则跳过,由于答案必然不会很大所以不会超时。

接着关键在于怎么判定该质因子满足条件并且求出转移次数。

这里用了一个技巧,现将b[i]=a[i]%prime[j] 求出, 然后对b  sort一下 然后从最左边l 和最右边r 开始 令tt=min(prime[j]-b[r], b[l])

然后令b[l]-=tt , b[r]+=tt (就是将前面补到后面) 若左边b[l]=0 则l右移 ,若右边b[r] =prime[j] 则r 左移

然后求所有结果的最小值 代码如下 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxl=500000+10;
bool check[maxl];
int prime[500005];
int a[100005];
int b[100005];
int tot=0;
long long ans=0;
long long Min;
int Max=-1;
int T;
int N;
int main()
{
    memset(check , 0 , sizeof(check));
    for(int i = 2 ; i < maxl ; ++i)
    {
        if(!check[i])prime[tot++] = i;
        for(int j = 0 ; j < tot ; j++)
        {
            if(i * prime[j] > maxl)break;
            check[i * prime[j]] = 1;
            if ( i % prime[j] == 0 )break;
        }
    }
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        Max=-1;
        scanf("%d",&N);
        for(int i=1;i<=N;i++)
        {
            scanf("%d",&a[i]);
            Max=max(Max,a[i]);
            ans+=a[i];
        }
        Min=ans-Max;
        for(int j=0;j<tot&&prime[j]<=ans;j++)
        {
            if(ans%prime[j]!=0)continue;
            for(int k=1;k<=N;k++)
            {
                b[k]=a[k]%prime[j];
            }
            sort(b+1,b+1+N);
            long long l=1;
            long long  r=N;
            long long result=0;
            while(l<r)
            {
                int tt=min(b[l],prime[j]-b[r]);
                b[l]-=tt;
                b[r]+=tt;
                result+=tt;
                if(b[l]==0)l++;
                if(b[r]==prime[j])r--;
            }
            Min=min(Min,result);
        }
        printf("%lld\n",Min);
    }
    return 0;

}

 

两个注意点:最大值的设置不要设为1e18  考虑到所有数的和可能刚好是一个很大的素数 此时所打的素数表可能不够整除,此时输出的结果必然是所有数的和减去最大的数(将其他石头都移到这个最大的堆上)所以要将Min设置为总和减去最大堆。(wa了八发)

不能直接统计对每个堆的最小操作数(会wa具体原因不知)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值