分析:
这道题就是一道公式推导题,我再来推一遍公式:
设
f(x)
函数:
f(x)={1,0,x是完全平方数否则
则
ANS=n×m−∑ni=1∑mj=1f(gcd(i,j))
这个时候只需要维护后面一块就可以了
TEST=∑i=1n∑j=1mf(gcd(i,j))=∑i=1n∑j=1m∑d=gcd(i,j)f(d)=∑d=1min(n,m)f(d)∑i=1n∑j=1m(gcd(i,j)=d)=∑d=1min(n,m)f(d)∑i=1⌊nd⌋∑j=1⌊md⌋(gcd(i,j)=1)=∑d=1min(n,m)f(d)∑i=1⌊nd⌋∑j=1⌊md⌋∑t|gcd(i,j)μ(t)=∑d=1min(n,m)f(d)∑t=1min(⌊nd⌋,⌊md⌋)μ(t)×⌊ndt⌋×⌊mdt⌋,=∑g=1min(n,m)⌊ng⌋×⌊mg⌋∑d|gμ(d)×f(gd)使g=dt
这个时候就搞定了,后面一块 ∑g∑d|gμ(d)×f(gd) 维护这个式子和,复杂度是 o(nlogn) 但是还可降低: ∑g∑x2|gμ(x2)×f(gx2)⟹∑x∑x2|gμ(d) ,这个时候复杂度就是 o(n) (复杂度计算就是平方的倒数和,泰勒展开)。
然后维护 ∑g∑d|gμ(d)×f(gd) 的前缀和,就可以分块做了,分块的复杂度 o(n√)
总的复杂度是 o(n+Tn√)
代码:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <string>
using namespace std;
typedef long long LL;
typedef vector <int> VI;
typedef pair <int,int> PII;
#define FOR(i,x,y) for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int maxn = 10000010;
int mu[maxn],prime[maxn];
bool check[maxn];
void Mobius(){
memset(check,false,sizeof(check));
prime[0] = 0;
mu[1] = 1;
FOR(i,2,maxn){
if(!check[i]) {mu[i] = -1;prime[++prime[0]] = i;}
FOR(j,1,prime[0]+1){
if(i*prime[j] > maxn) break;
check[i*prime[j]] = true;
if(i% prime[j] == 0) {mu[i*prime[j]] = 0;break;}
else {mu[i*prime[j]] = -mu[i];}
}
}
}
LL sum[maxn];
int n,m;
LL calc(int x,int y){
LL ans = 0;
int i = 1;
while(i <= x){
int p = x/i,q = y/i;
int j = min(x/p,y/q);
ans += (LL)p*q*(sum[j]-sum[i-1]);
i = j+1;
}
return ans;
}
int main(){
Mobius();
memset(sum,0,sizeof(sum));
FOR(i,1,maxn){
int k = i*i;
if(k >= maxn) break;
for(int j = k;j < maxn;j += k) sum[j] += mu[j/k];
}
FOR(i,1,maxn) sum[i] += sum[i-1];
int T; scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
if(n < m) swap(n,m);
printf("%I64d\n",(LL)n*m-calc(m,n));
}
return 0;
}