Hdu 4611 数论

来源:互联网 发布:网络枪战手游排行榜 编辑:程序博客网 时间:2024/06/06 05:17
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 (其实没有用)

  但这样对于样例第一个这种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;}



原创粉丝点击