Description
神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种
傻×必然不会了,于是向你来请教……多组输入
Input
第一行一个整数T 表述数据组数接下来T行,每行两个正整数,表示N, M
Output
T行,每行一个整数表示第i组数据的结果
Sample Input
2
10 10
100 100
Sample Output
30
2791
HINT
T = 10000
N, M <= 10000000
Source
题解
前置知识:莫比乌斯反演
题目让我们求这个:
[ans=sum_{i=1}^nsum_{j=1}^m [gcd(i,j) in pri]\
]
其中(pri)表示质数集合。
然后枚举这个质数:
[egin {align}
ans&= sum _{din pri} sum _{i=1}^{n} sum _{j=1}^{m} [gcd(i,j)=d]\
&=sum _{din pri} sum _{i=1}^{lfloor frac{n}{d}
floor} sum _{j=1}^{lfloor frac{m}{d}
floor} [gcd(i,j)=1] \
end {align}
]
根据莫比乌斯函数的性质可以得到:
[sum _{d ^prime|n} mu(d ^prime)=[n=1]
]
然后把(n)换成(gcd(i,j))带进去,得:
[ans=sum _{din pri} sum _{i=1}^{lfloor frac{n}{d}
floor} sum _{j=1}^{lfloor frac{m}{d}
floor} sum _{d^prime|i &d^prime|j} mu (d^prime)
]
然后先枚举(d^prime),得:
[egin{align}
ans&=sum _{din pri} sum _{d^prime} mu (d^prime) sum _{i=1}^{lfloor frac{n}{dd^prime}
floor} sum _{j=1}^{lfloor frac{m}{dd^prime}
floor} 1\
ans&= sum _{din pri} sum _{d^prime} mu(d^prime) lfloor frac{n}{dd^prime}
floor lfloorfrac{m}{dd^prime}
floor
end {align}
]
令(T=dd^prime),得:
[egin{align}
ans&= sum _{d in pri } sum _{T}mu(frac{T}{d}) lfloor frac{n}{T}
floor lfloorfrac{m}{T}
floor \
&= sum _{T}lfloor frac{n}{T}
floor lfloorfrac{m}{T}
floor sum _{d in pri & d|T} mu(frac{T}{d})
end {align}
]
[egin {align}
& ext{令}f(n) = sum _{din pri & d|T} mu (d) \
&ans= sum _{T}lfloor frac{n}{T}
floor lfloorfrac{m}{T}
floor f(T)
end {align}
]
线筛出质数和(mu)函数之后,暴力预处理(f)并求出前缀和,然后数论分块就行了。
由于n以内的质数只有(O(n/ln(n)))个,所以预处理复杂度为(O(n/ln(n)*ln(n/ln(n))+n)=O(n)),具体参考调和级数。
然后询问的复杂度为(O(qsqrt{n}))。
#include<bits/stdc++.h>
using namespace std;
#define int long long
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
const int maxn = 1e7+1;
int mu[maxn+10],sum[maxn+10],pri[maxn+10],tot,vis[maxn+10],n,m;
void time() {cerr << (double) clock()/CLOCKS_PER_SEC << endl;}
void sieve() {
mu[1]=1;
for(int i=2;i<maxn;i++) {
if(!vis[i]) pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<maxn;j++) {
vis[i*pri[j]]=1;
if(!(i%pri[j])) {mu[i*pri[j]]=0;break;}
mu[i*pri[j]]=-mu[i];
}
}//time();write(tot);
for(int i=1;i<=tot;i++)
for(int j=1;j*pri[i]<maxn;j++) sum[j*pri[i]]+=mu[j];
for(int i=1;i<maxn;i++) sum[i]=sum[i]+sum[i-1];
//time();
}
signed main() {
sieve();int asd;read(asd);
//for(int i=1;i<=10;i++) write(sum[i]);
while(asd--) {
read(n),read(m);
if(n>m) swap(n,m);
int T=1,ans=0;
while(T<=n) {
int pre=T;T=min(n/(n/T),m/(m/T));
ans+=(n/pre)*(m/pre)*(sum[T]-sum[pre-1]);
T++;
}
write(ans);
}
return 0;
}