2013 多校第二场 hdu 4611 Balls Rearrangement

hdu 4611

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4611

题目大意:题目意思说起来挺烦的,其实就是让你算 ABS(i % a - i % b)的和,i 的范围是 10^9 。

思路:比赛的时候想到最小公倍数了,也想到一段一段来了,可就是不知道哪里脑子抽了,还想整理一下,推出一个公式来。其实,现在反过来想想真心不用,a、b小的时候,根据 LCM 来求,复杂度很小,如果 a、b 大,那么一段一段来很快的。事实证明这样算只需要 15ms 。先开始也WA了几次,是有的地方超 int ,没有强制转化的问题。

貌似也有数学公式,对于不懂数论的我来说,这种做法在效率和思维上已经都可以了,如果有机会的话,再学一下公式吧。。

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

typedef __int64 lld;

int gcd(int a,int b)
{
    if(a==0 ) return b;
    return gcd(b%a,a);
}

lld cal(int n,int a,int b)
{
    lld  ans =0 ;
    for(int i=0;i<n;)
    {
        int t1 = i%a,t2 = i%b;
        int p1 = a-t1,p2 = b-t2;
        if(p1<p2)
        {
            ans += (lld)abs(t1-t2)*p1;
            i = i + p1;
        }
        else
        {
            ans += (lld)abs(t1-t2)*p2;
            i = i + p2;
        }
        if(i>=n)
        {
            int h1 = (n-1)%a,h2 = (n-1)%b;
            if(p1<p2)
                ans -= (lld)abs(t1-t2)*(a-1-h1);
            else ans -=(lld)abs(t1-t2)*(b-1-h2);
        }
    }
    //printf("ans = %I64d,n = %d\n",ans,n);
    return ans;
}

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


你可能感兴趣的:(2013 多校第二场 hdu 4611 Balls Rearrangement)