Hdu 4611 数论(输出答案注意%I64d而不是%lld)
x≡a(mod A), x≡b(mod B)
nA+a=x, mB+b=x;
nA+a=mB+b
|nA-mB|=|a-b|
诡异,跟x没有关系!!!
其实关系是x∈[nA,(n+1)A-1] &&x∈[mB,(m+1)B-1] (由a,b的上下界和nA+a=x, mB+b=x;推出)
于是变成一段一段算了,因为同个段里的单个值是一样的。
然后想法是枚举n,m,但这样还是n^2,可以枚举n,算出m的下界,m到达上界时及时break
枚举n/a,再乘上a/b 大致是n/b ,可以swap保证a<b (其实没有用)
x≡a(mod A), x≡b(mod B)
nA+a=x, mB+b=x;
nA+a=mB+b
|nA-mB|=|a-b|
诡异,跟x没有关系!!!
其实关系是x∈[nA,(n+1)A-1] &&x∈[mB,(m+1)B-1] (由a,b的上下界和nA+a=x, mB+b=x;推出)
于是变成一段一段算了,因为同个段里的单个值是一样的。
然后想法是枚举n,m,但这样还是n^2,可以枚举n,算出m的下界,m到达上界时及时break
枚举n/a,再乘上a/b 大致是n/b ,可以swap保证a<b (其实没有用)
但这样对于样例第一个这种n比较大,a,b均比较小的情况还是会跪的。a,b均比较小此时lcm也很小,可以分情况讨论了。
#include<cstdio>
#include<cstring>
#include<cstdlib>
typedef long long LL;
LL n,a,b,ans,lcm;
LL gcd(LL a,LL b)
{ return a%b==0?b:gcd(b,a%b);
}
LL abs(LL a)
{ return a>=0?a:-a;
}
LL min(LL a,LL b)
{ return a>b?b:a;
}
LL max(LL a,LL b)
{ return a<b?b:a;
}
void doit1()
{ LL t;
for (int i=0;i<lcm;i++)
{if (i>=n) break; //因为编号为0~n-1
t=(n-1-i)/lcm+1;
ans+=abs(i%a-i%b)*t;
}
}
void doit2()
{ LL q,l,r,ll,rr;
for (int i=0;;i++)
{
l=a*i; r=a*(i+1)-1;
if (r>=n) r=n-1;
if (l>=n) break;
q=l/b;
for (int j=q;;j++)
{
ll=b*j;
rr=b*(j+1)-1;
if (min(rr,r)<max(ll,l)) break;
ans+=abs((a*i-b*j))*(min(rr,r)-max(ll,l)+1);
}
}
}
void doit()
{ scanf("%lld%lld%lld",&n,&a,&b);
lcm=a*b/gcd(a,b);
ans=0;
if (lcm<1000000)doit1();else doit2();
printf("%I64d\n",ans); //注意!!
}
int main()
{ int cas;
scanf("%d",&cas);
while (cas--)doit();
return 0;
}