设$$f(k)=sum_{i=1}^{a}sum_{j=1}^{b}[gcd(i,j)=k]$$
$$g(n)=sum_{n|k}f(k)=lfloorfrac{a}{n} floorlfloorfrac{b}{n} floor$$
根据莫比乌斯反演定理可以推出$$f(n)=sum_{n|k}mu(lfloorfrac{k}{n} floor)g(k)$$
那么可以发现$ans=f(d)$
然后用推出来的结论带进去
$$ans=sum_{d|k}mu(lfloorfrac{k}{d} floor)g(k)$$
枚举$lfloorfrac{k}{d} floor$设为$t$
$$ans=sum_{t=1}^{min(lfloorfrac{a}{d} floor,lfloorfrac{b}{d} floor)}mu(t)lfloorfrac{a}{td} floorlfloorfrac{b}{td} floor$$
对于$lfloorfrac{a}{td} floorlfloorfrac{b}{td} floor$相同的一段我们可以直接用前缀和算出答案
总而言之就是先预处理出$mu$的前缀和然后用整除分块,那么每一次询问的复杂度就是$O(sqrt{n})$
1 //minamoto 2 #include<cstdio> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 7 char buf[1<<21],*p1=buf,*p2=buf; 8 inline int read(){ 9 #define num ch-'0' 10 char ch;bool flag=0;int res; 11 while(!isdigit(ch=getc())) 12 (ch=='-')&&(flag=true); 13 for(res=num;isdigit(ch=getc());res=res*10+num); 14 (flag)&&(res=-res); 15 #undef num 16 return res; 17 } 18 const int N=6e5+5; 19 int p[N],mu[N],vis[N],m,sum[N];ll ans,lim; 20 void init(int n){ 21 mu[1]=1; 22 for(int i=2;i<=n;++i){ 23 if(!vis[i]) mu[i]=-1,p[++m]=i; 24 for(int j=1;j<=m&&p[j]*i<=n;++j){ 25 vis[i*p[j]]=1; 26 if(i%p[j]==0) break; 27 mu[i*p[j]]=-mu[i]; 28 } 29 } 30 for(int i=1;i<=n;++i) sum[i]=sum[i-1]+mu[i]; 31 } 32 int main(){ 33 // freopen("testdata.in","r",stdin); 34 int n,m,T,d;scanf("%d",&T); 35 init(50000); 36 while(T--){ 37 scanf("%d%d%d",&n,&m,&d);ans=0; 38 lim=min(n/d,m/d); 39 40 for(int l=1,r;l<=lim;l=r+1){ 41 r=min(n/(n/l),m/(m/l)); 42 ans+=1ll*(n/(l*d))*(m/(l*d))*(sum[r]-sum[l-1]); 43 } 44 printf("%lld ",ans); 45 } 46 return 0; 47 }