题目链接:https://vjudge.net/problem/HDU-4710
转自:https://blog.csdn.net/u011523796/article/details/11542497?utm_source=app
题意:有n个篮球,a个第一种篮子,b个第二种篮子。把篮球编号为0~n-1,把篮子分别编号为0 ~a-1,0 ~b-1。设篮球编号为x,把每个篮球放入编号为x%a的第一种篮子中,在把篮球放入编号为x%b的第二种篮子中,花费为原来所在篮子的编号-后来所在篮子的编号的绝对值,求总花费。
思路:1e9的数量级,直接遍历一遍都超时。有两个优化的地方:1.发现球的花费以a与b的最小公倍数为一个周期 2.若x是篮球编号,则从满足x%a=0||x%b=0的x开始到下一个x%a=0||x%b=0的前一位,它们的花费都是相同的。
#include <bits/stdc++.h>
using namespace std;
long long cal(long long n,long long a,long long b)
{
long long now=0,temp=0,x=0,y=0,ans=0;
while(now<n)
{
temp=min(a-x,b-y);//temp记录每次往后移动的位数 取最小值
//也就是距离下一个x%a=0||x%b=0的距离
if(now+temp>=n)//如果超出,就减去超出的部分
{
temp=n-now;
}
ans+=temp*abs(x-y);//统计答案
x=(x+temp)%a;//x与y分别记录在0~a-1和0~b-1的位置
y=(y+temp)%b;
now+=temp;//now记录当前总位数
}
return ans;
}
int main()
{
long long t,n,a,b;
cin>>t;
while(t--)
{
cin>>n>>a>>b;
long long l=a*b/__gcd(a,b);//计算最小公倍数
if(l>=n)//如果最小公倍数大于篮球数则不能按照公倍数拆分
{
cout<<cal(n,a,b)<<endl;
}
else//可以按照公倍数进行分块
{//两部分分别为:一个周期内的花费*一共可以被分成几个周期+多余的
//此处一定要注意n/l要加(),因为计算次序的问题所以一定要加
cout<<cal(l,a,b)*(n/l)+cal(n%l,a,b)<<endl;
}
}
}