HDU 4710 Balls Rearrangement (纯数学)

题目链接:HDU 4710 Balls Rearrangement

题意:不啰嗦了;

提炼出来就是求


思路:

容易得到结果就是 一个数列的是以lcm(a,b)为循环节的一个数列,答案就是求和这个数列。扫一遍的话lcm(a,b)很大会爆掉(比赛爪机的在一个循环节中for了一遍 = =)。

大神的做法:在算一个循环节中跳着求和,比如: 20 5 3 



红色框(代码中的t)中是是以 %a为0到 %b为0,%b为0到 %a为0。这一段段中的花费是一样的。(这里就减少时间复杂度)。

解释下为什么是花费一样:seq数列是等差递增的,那么这段数列( 指的是 %a为0到 %b为0  或者 %b为0到 %a为0 )对a,b取摸后的值递增是不变的,所以上(%a)下(%b)的差不变。

x,y的差就是算相同花费中的一个花费



#include <stdio.h>
#define LL __int64
#include <algorithm>
using std::min;
LL iabs(LL a)
{
    if(a<0) return -a;
    return a;
}
LL find(LL a,LL b,LL c)
{
    LL x=0,y=0,t,p=0,q;
    LL ans=0;
    while(p<c)
    {
        t=min(a-x,b-y);
        if(t+p>=c)
            t=c-p;
        ans+=t*abs((LL)(y-x));
        x=(x+t)%a;
        y=(y+t)%b;
        p+=t;
    }
    return ans;
}
LL gcd(LL a,LL b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}
LL lcm(LL a,LL b)
{
    return a/gcd(a,b)*b;
}
int main()
{
    LL ans;
    LL t,sum;
    LL n,a,b,i,g;
    scanf("%I64d",&t);
    while(t--)
    {
        scanf("%I64d%I64d%I64d",&n,&a,&b);
        g=lcm(a,b);
        sum=0;
       /* p[1]=p[0]=0;
        for(i=0;i<g;i++)
        {
            sum+=iabs((i%a)-(i%b));
            p[i]=iabs((i%a)-(i%b));
        }*/
		/*for(i=0;i<g;i++)
			printf("%I64d ",p[i]);
		printf("\n");*/
        ans=find(a,b,g)*(n/g)+find(a,b,n%g);
        printf("%I64d\n",ans);
    }
    return 0;
}
/*
10
20 2 4
30 5 3
8 2 4
11 5 3
*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章