BZOJ 1101: [POI2007]Zap
1101: [POI2007]Zap
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2262 Solved: 895
[Submit][Status][Discuss]
Description
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a
,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
Input
第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个
正整数,分别为a,b,d。(1<=d<=a,b<=50000)
Output
对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。
Sample Input
2
4 5 2
6 4 3
4 5 2
6 4 3
Sample Output
3
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。
HINT
Source
分析:
其实相当于求n/d,m/d限制下,gcd(i,j)==1的ij个数
做法与http://www.cnblogs.com/neighthorn/p/6214769.html相同...
需要注意的是这题O(n)肯定过不了...没有看数据范围的我先TLE了一发...
因为(n/i)*(m/i)的取值最多有sqrt(n)+sqrt(m)种,所以我们预处理μ的前缀和,分段计算就好了...
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 using namespace std; 7 //大鹏一日同风起,扶摇直上九万里 8 9 const int maxn=50000+5; 10 11 int n,m,d,cas,cnt,miu[maxn],vis[maxn],prime[maxn]; 12 13 long long ans=0; 14 15 signed main(void){ 16 memset(vis,0,sizeof(vis));cnt=0;miu[1]=1; 17 for(int i=2;i<=50000;i++){ 18 if(!vis[i]) 19 prime[++cnt]=i,vis[i]=1,miu[i]=-1; 20 for(int j=1;j<=cnt&&prime[j]*i<=50000;j++){ 21 vis[i*prime[j]]=1; 22 if(i%prime[j]==0){ 23 miu[i*prime[j]]=0;break; 24 } 25 miu[i*prime[j]]=-miu[i]; 26 } 27 } 28 for(int i=1;i<=50000;i++) 29 miu[i]+=miu[i-1]; 30 scanf("%d",&cas); 31 while(cas--){ 32 scanf("%d%d%d",&n,&m,&d); 33 n/=d,m/=d;ans=0; 34 if(n>m) 35 swap(n,m); 36 for(int i=1,r;i<=n;i=r+1){ 37 r=min(n/(n/i),m/(m/i)); 38 ans+=(long long)(miu[r]-miu[i-1])*(n/i)*(m/i); 39 } 40 printf("%lld\n",ans); 41 } 42 return 0; 43 }
by NeighThorn