HDU 4611Balls Rearrangement(思维)

时光总嘲笑我的痴心妄想 提交于 2020-03-13 11:03:26

Balls Rearrangement

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

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.
 

 

Sample Input
3 1000000000 1 1 8 2 4 11 5 3
 

 

Sample Output
0 8 16
 

 

Source
 

                题目大意:刚开始比赛的时候,一看见这题,以为是水题。是自己想简单了。题目是这样:给你n个球编号0~n-1,先放进a个箱子里,i mod a =p,第i个球放在箱子p里面,就是同模的放在一起。然后再把这些球移到b个箱子里面,同样的法则。sum值等于每个球从i号箱子移到j号箱子的数目总和。一个球的sum=abs(i-j).

          解题思路:开始把他想简单了,这些球都是有唯一的标识的。后来想清楚了之后,发现循环节是mi(n,lcm(a,b))但是直接用循环节统计会TLE。如果a取9999,b取9997,n=10^9那么循环节直接是10^9。肯定会TLE。然后又开始找规律,突然发现可以用两个指针不断地移动。不用每次都是++,自己可以在纸上画一下。
0 1 2 3 4     0 1 2
5 6 7 8 9     3 4 5
10            6 7 8
              9 10
如果到6的时候,左边一行可以到9,右边一行可以到8.我们选取小的。会发现6~8之间abs(i-j)是一样的。同理0可以直接跳到3那里。主要就是用到这个思想。详见代码。当时因为没有用__int64Wa了好半天!!!

          题目地址:Balls Rearrangement

AC代码:
0 1 2 3 4     0 1 2
5 6 7 8 9     3 4 5
10            6 7 8
              9 10

#include<iostream>
#include<cstdio>
using namespace std;

__int64 f(__int64 x)
{
    if(x<0)
        x=-x;
    return x;
}
__int64 gcd(__int64 m,__int64 n)
{
    __int64 t;
    while(n)
    {
        t=m%n; m=n; n=t;
    }
    return m;
}

__int64 min1(__int64 x1,__int64 x2)
{
    if(x1<x2) return x1;
    else return x2;
}

int main()
{
    __int64 mi,p,a,b,i,j,t1,t2,cnt,sum,tes;
    __int64 lcm;
    scanf("%I64d",&tes);
    while(tes--)
    {
        scanf("%I64d%I64d%I64d",&p,&a,&b);
        if(a==b)
        {
           puts("0");
           continue;
        }
        lcm=a/gcd(a,b)*b;   //a,b的最小公倍数
        mi=p;
        if(mi>lcm)
            mi=lcm;   //mi是循环节
        t1=p/mi,t2=p%mi;
        cnt=0,i=0,j=0;
        sum=0;

        __int64 mi2;
        while(cnt<mi)  //一个循环内的
        {
            if(i>=a) i-=a;
            if(j>=b) j-=b;
            mi2=min1(a-i,b-j);
            if(mi2>mi-cnt)  //这点需要注意,不能超过步数
                mi2=mi-cnt;
            sum+=f(i-j)*mi2;
            i+=mi2; j+=mi2; cnt+=mi2;
        }
        sum=sum*t1;
        cnt=0,i=0,j=0;
        while(cnt<t2)  //循环节以外的
        {
            if(i>=a) i-=a;
            if(j>=b) j-=b;
            mi2=min1(a-i,b-j);
            if(mi2>t2-cnt)
                mi2=t2-cnt;
            sum+=f(i-j)*mi2;
            i+=mi2; j+=mi2; cnt+=mi2;
        }
        printf("%I64d\n",sum);
    }
    return 0;
}

/*
23
1000000000 1 1
8 2 4
11 5 3
1000000000 9999 9997
*/


 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!