首页 > 代码库 > hdu 4710 Balls Rearrangement (数学思维)

hdu 4710 Balls Rearrangement (数学思维)

题意:就是  把编号从0-n的小球对应放进i%a编号的盒子里,然后又买了新盒子,

            现在总共有b个盒子,Bob想把球装进i%b编号的盒子里。求重置的最小花费。

            每次移动的花费为y - x ,即移动前后盒子编号的差值的绝对值。


算法:

题目就是要求                           



先判断  n与  lcm(a,b)的大小,每一个周期存在循环,这样把区间缩短避免重复计算。

如果n>lcm(a,b)则   ans = (n/lcm)*solve(lcm)+solve(n%lcm)

否则   ans = solve(n)


设x和y分别等于  i%a和i%b,

我们通过枚举 找规律能发现  t=min(a-x,b-y)是一个段,这一段内abs(x-y)是相等的。

所以只需要用abs(x-y)乘以次数t,在算下一段就行了。


这里要注意t<n-now的情况。本来这一段应该有t个,但是now+t>n了,所以要取t = min(t,n-now)


#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>

using namespace std;

typedef __int64 ll;

ll a,b;

ll Gcd(ll x,ll y)
{
    return y==0?x:Gcd(y,x%y);
}

ll abs(ll x)
{
    return x>=0?x:(-x);
}

ll solve(ll n)
{
    ll x=0,y=0,t,v1,v2,now=0;
    ll ret = 0;
    while(now<n)
    {
        v1 = a-x;
        v2 = b-y;
        t = min(v1,v2);
        t = min(t,n-now);
        ret += t*abs(x-y);
        x = (x+t)%a;
        y = (y+t)%b;
        now += t;
    }
    return ret;
}

int main()
{
    int T;
    ll n,gcd,lcm,ans;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d%I64d%I64d",&n,&a,&b);
        gcd = Gcd(a,b);
        lcm = a*b/gcd;
        if(n>=lcm)
            ans = solve(lcm)*(n/lcm) + solve(n%lcm);
        else ans = solve(n);
        printf("%I64d\n",ans);
    }
    return 0;
}

蒟蒻真心不适合  搞数学  o(╯□╰)o