HDU 4710 Balls Rearrangement (纯数学)

来源:互联网 发布:macbook pro卸载软件 编辑:程序博客网 时间:2024/05/16 08:34

题目链接: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;}/*1020 2 430 5 38 2 411 5 3*/


1 0