您的位置:首页 > 其它

Zap [bzoj 1101,POI 2007]

2016-06-05 21:15 302 查看
题目地址请点击——

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<=⌊ak⌋,y<=⌊bk⌋ 的对数。

∴ans=∑i=1nμ(i)⋅⌊⌊ak⌋i⌋⋅⌊⌊bk⌋i⌋

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

【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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: