题意:
有N个球,编号为0~n-1,一开始有A个箱子,编号0~A-1,将球x放到x%A那个箱子里。现在有B个新箱子,要把球x放到x%B那个箱子里,代价是|x%A-x%B|,求总代价。
题解:
N比较打所以不能暴力,可以这么想:
如果x`=x+LCM(A,B),那么|x%A-x%B|=|x`%A-x`%B|成立,且使得该等式成立的x和x`的最小差值也是LCM(A,B)。也就是在[0,LCM(A,B)]这个区间里,|x%A-x%B|遍历完所有可能的值。
连续的一段x中,当x%A和x%B没有发生跳变(从A-1变成0,B同理)时,|x%A-x%B|是相同的,即[0,LCM(A,B)]又可以分为一些小区间,每个小区间|x%A-x%B|是相同的。由于x%A只在x%A=A-1后跳变为0,所以总跳变数小于LCM(A,B)/A+LCM(A,B)/B,这样暴力找全部小区间是可以接受的,找到一个小区间后加上LCM(A,B)又是一个同样的区间。
//Time:109ms
//Memory:352KB
//Length:1065B
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define MAXN 620
#define INF 10007
using namespace std;
int gcd(int a,int b)
{
while(a%b!=0)
a%=b,swap(a,b);
return b;
}
int main()
{
//freopen("/home/moor/Code/input","r",stdin);
int ncase,n,a,b;
long long lca,l,la,lb,tt,ans,tmp,step;
scanf("%d",&ncase);
while(ncase--)
{
ans=0;
scanf("%d%d%d",&n,&a,&b);
lca=(long long)a*b/gcd(a,b);
l=0,la=a,lb=b;
for(;l<n;)
{
tt=min(min(la,lb),(long long)n);
tmp=abs(l%a-l%b);
step=(n-l+lca-1)/lca;
if((n-l)%lca<tt-l&&(n-l)%lca!=0)
ans+=((n-l)%lca)*step*tmp+(tt-l-(n-l)%lca)*(step-1)*tmp;
else ans+=tmp*(tt-l)*step;
l=tt;
if(la==lb) break;
if(l==la) la+=a;
else lb+=b;
}
cout<<ans<<'\n';
}
return 0;
}