2013 多校联合 2 A Balls Rearrangement (hdu 4611)

Balls Rearrangement

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 322    Accepted Submission(s): 114


Problem Description

 

  Bob has N balls and A boxes. He numbers the balls from 0 to N-1, and numbers the boxes from 0 to A-1. To find the balls easily, he puts the ball numbered x into the box numbered a if x = a mod A.   Some day Bob buys B new boxes, and he wants to rearrange the balls from the old boxes to the new boxes. The new boxes are numbered from 0 to B-1. After the rearrangement, the ball numbered x should be in the box number b if x = b mod B.
  This work may be very boring, so he wants to know the cost before the rearrangement. If he moves a ball from the old box numbered a to the new box numbered b, the cost he considered would be |a-b|. The total cost is the sum of the cost to move every ball, and it is what Bob is interested in now.
 


 

Input

 

  The first line of the input is an integer T, the number of test cases.(0<T<=50)  
  Then T test case followed. The only line of each test case are three integers N, A and B.(1<=N<=1000000000, 1<=A,B<=100000).
 


 

Output

 

  For each test case, output the total cost.


 

思路:我们可以发现循环节为a,b的最小公倍数tmp,所以我们只要求从1到tmp的花费即可,易知a和b的大小关系对题目答案没有影响,不妨设a>b,设从1到tmp的花费依次为 w1,w2,w3,....wtmp,我们可以将这tmp个值分成每b个一组,一共a/gcd组(gcd为a和b的最大公约数),我们设num[i]为第i组的和,现在的关键问题是num[i]怎么求,对于每一组num[i],这时第一个数在a盒子中的第po个,那么它的花费就是po-1(因为这个数一定是在b盒子的第一个),那么对于后面的第2个,第三个。。。。。。的花费也将是po-1,直到走到第po+b-1个,或者到第a个盒子(其实就是a和po+b-1去个小),若走到了第po+b-1个盒子,则这一组已经算完,为b*(po-1)花费,且po将被更新为po+b,否则,还应该计算剩下来的花费,这个和前一半类似,只不过这是a盒子从1开始,b盒子从a-po+2开始而已,这也是一串相同的花费,可以O(1)求出,然后更新po。将所有组求完后,剩下的工作就简单了。这里不再罗嗦。

以下是代码。

 

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define ll long long
using namespace std;
ll dp[100010],sum[100010];
int Po[100010];
long long GCD(long long a,long long b)
{
    if(b==0)
    return a;
    return GCD(b,a%b);
}
ll getans(ll x)
{
    if(x>0)
    return x;
    return -x;
}
int main()
{
    //freopen("dd.txt","r",stdin);
    int ncase;
    scanf("%d",&ncase);
    while(ncase--)
    {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        ll n,a,b;
        cin>>n>>a>>b;
        if(a<b)
        swap(a,b);
        if(a==b)
        printf("0\n");
        else
        {
            ll tmp=GCD(a,b);
            ll tt=tmp;
            tmp=a*b/tmp;
            dp[1]=0;
            sum[1]=0;
            ll po=b+1;
            Po[1]=1;
            Po[2]=b+1;
            for(int i=2;i<=a/tt;i++)
            {
                if(po+b-1<=a)
                {
                    dp[i]=(po-1)*b;
                    po=po+b;
                    if(po>a)
                    po-=a;
                }
                else
                {
                    dp[i]=(po-1)*(a-po+1);
                    int tt=a-po+2;
                    dp[i]+=(tt-1)*(b-tt+1);
                    po=b-tt+2;
                    if(po>a)
                    po-=a;
                }
                Po[i+1]=po;
                sum[i]=sum[i-1]+dp[i];
            }

            ll ans=0;
            ans=sum[a/tt]*(n/tmp);
            n%=tmp;
           // cout<<n<<endl;
            ll x=n/b;
            ans+=sum[x];
            n%=b;
            if(n)
            {
                ll PO=Po[x+1];
                for(int i=1;i<=n;i++)
                {
                    ans+=getans(PO-i);
                    PO++;
                    if(PO>a)
                    PO-=a;
                }
            }
            cout<<ans<<endl;
        }

    }
    return 0;
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值