p3455 [poi2007]zap


双倍经验就是爽.jpg

思路

和YY的GCD类似但是更加简单了
类似的推一波公式即可
$$
F(n)=sum_{n|d}f(d)
$$

$$
f(n)=sum_{n|d}mu(frac{d}{n})F(d)
$$

$$
F(d)=lfloorfrac{n}{d}rfloortimeslfloorfrac{m}{d}rfloor
$$

$$
f(x)=sum_{x|d}mu(frac{d}{x})timeslfloorfrac{n}{d}rfloortimeslfloorfrac{m}{d}rfloor
$$

$$
f(x)=sum_{t=1}^{lfloorfrac{n}{x}rfloor}mu(t)timeslfloorfrac{n}{xtimes t}rfloortimeslfloorfrac{m}{xtimes t}rfloor
$$
然后整除分块即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#include <algorithm>
#include <cstring>
using namespace std;
int T,n,m,mu[51000],iprime[51000],isprime[51000],summu[51000],cnt,k;
void (int n){
isprime[1]=true;
mu[1]=1;
for(int i=2;i<=n;i++){
if(!isprime[i])
iprime[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&iprime[j]*i<=n;j++){
isprime[iprime[j]*i]=true;
mu[iprime[j]*i]=-mu[i];
if(i%iprime[j]==0){
mu[iprime[j]*i]=0;
break;
}
}
}
for(int i=1;i<=n;i++)
summu[i]=summu[i-1]+mu[i];
}
long long f(int k){
long long ans=0;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min((n/(n/(l))),(m/(m/(l))));
ans+=1LL*(summu[r]-summu[l-1])*(n/(l*k))*(m/(l*k));
}
return ans;
}
int main(){
prime(50100);
scanf("%d",&T);
while(T--){
scanf("%d %d %d",&n,&m,&k);
if(n<m)
swap(n,m);
printf("%lldn",f(k));
}
return 0;
}