「luogu3455」[POI2007]ZAP-Queries
原问题转化为求有多少个二元组(x,y)满足 x≤a/k,y≤b/k 且 gcd(x,y)=1。
我们获得两个函数f(i)和g(i)。
f(i)表示满足 x≤a,y≤b 且 gcd(x,y)=i 的二元组个数,g(i)表示满足 x≤a,y≤b 且 gcd(x,y) 为 i 的倍数个二元组个数。
发现 g(i)=a/i*b/i,于是可以用莫比乌斯反演求出f(i)。
但是一个一个求g(i)时会超时,考虑优化。
发现g(i)有很多段的值都是一样的,可以求出莫比乌斯函数的前缀和后计算一整段的答案。
也就是数论分块。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=50010; 4 int n,a,b,k,u[N],prime[N],tot; 5 bool isp[N]; 6 inline void getu(int k){ 7 u[1]=1; 8 for(int i=2;i<=k;i++){ 9 if(!isp[i]) prime[++tot]=i,u[i]=-1; 10 for(int j=1;j<=tot&&1LL*prime[j]*i<=k;j++){ 11 u[prime[j]*i]=-u[i],isp[prime[j]*i]=1; 12 if(!(i%prime[j])){u[prime[j]*i]=0;break;} 13 } 14 } 15 for(int i=2;i<=k;i++) u[i]+=u[i-1]; 16 return; 17 } 18 19 void solve(){ 20 scanf("%d%d%d",&a,&b,&k); 21 a/=k,b/=k; 22 if(a>b) swap(a,b); 23 int ans=0; 24 int now=1,nxt; 25 while(now<=a){ 26 nxt=min(a/(a/now),b/(b/now)); 27 ans+=(u[nxt]-u[now-1])*(a/now)*(b/now); 28 now=nxt+1; 29 } 30 printf("%d\n",ans); 31 return; 32 } 33 int main(){ 34 scanf("%d",&n); 35 getu(50000); 36 while(n--) solve(); 37 return 0; 38 }