Zap [bzoj 1101,POI 2007]

来源:互联网 发布:淘宝优惠群怎么起名字 编辑:程序博客网 时间:2024/05/16 04:40

题目地址请点击——


Zap


【题目描述】

FGD正在破解一段密码,他需要回答很多类似的问题:
对于给定的整数 a , bd,有多少正整数对 (x,y),满足 x<=ay<=b,并且 gcd(x,y)=d
作为FGD的同学,FGD希望得到你的帮助。


【输入描述】

第一行包含一个正整数 n,表示一共有 n 组询问。(1<=n<=50000
接下来 n 行,每行表示一个询问,每行三个正整数,分别为 a,b,d1<=d<=a,b<=50000


【输出描述】

对于每组询问,输出一个正整数,表示满足条件的整数对数。


【样例输入】

2
4 5 2
6 4 3


【样例输出】

3
2


【Solution】

gcd(x,y)=kx<=a,y<=b 的对数,等价于求 gcd(x,y)=1x<=aky<=bk 的对数。

ans=i=1nμ(i)akibki

维护一下 μ 函数的前缀和,然后就可以分块优化了。


【Code】

#include <iostream>#include <cstdio>#define LL long long#define Min(x,y) ((x)<(y)?(x):(y))using namespace std;LL T,ans;LL a,b,d;LL sum[50010];//LL nxt[50010];short miu[50010];LL prime[50010];bool no_prime[50010];inline LL in(){    LL ans=0;    char x=getchar();    while(x<'0'||x>'9')x=getchar();    while(x>='0'&&x<='9'){ans=ans*10+x-'0';x=getchar();}    return ans; }void f(){    if(a>b)swap(a,b);    for(LL i=1,it;i<=a;i=it+1){        it=Min(a/(a/i),b/(b/i));        ans+=((a/i))*((b/i))*(sum[it]-sum[i-1]);    }}int main(){    scanf("%lld",&T);    miu[1]=1;    for(LL i=2;i<=50001;i++){        if(!no_prime[i]){            prime[++prime[0]]=i;            miu[i]=-1;         }        for(LL j=1;prime[j]*i<=50001;j++){            no_prime[prime[j]*i]=true;            if(i%prime[j]==0){                miu[prime[j]*i]=0;                break;            }            miu[prime[j]*i]=-miu[i];        }    }//  for(LL i=1;i<=50001;i++)if(miu[i]!=0)nxt[++nxt[0]]=i;    for(LL i=1;i<=50001;i++)sum[i]+=sum[i-1]+miu[i];    while(T--){        ans=0;        a=in();b=in();d=in();        a/=d;b/=d;        f();//      LL minx=Min(a,b);//      LL tmp=0;//      while(1){//          tmp++;//          LL rk=d*nxt[tmp];//          if(rk>minx)break;//          ans+=miu[nxt[tmp]]*((a/rk)*(b/rk));//      }        printf("%lld\n",ans);    }    return 0;}
0 0
原创粉丝点击