Zap [bzoj 1101,POI 2007]

题目地址请点击——


Zap


【题目描述】

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


【输入描述】

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


【输出描述】

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


【样例输入】

2
4 5 2
6 4 3


【样例输出】

3
2


【Solution】

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

ans=i=1nμ(i)akibki

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


【Code】

#include 
#include 

#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;
}

你可能感兴趣的:(题解,数学,BZOJ)