题意
给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对。
T(组数) = 10000 N,M<=10000000
题解
如果我们枚举质数
p=gcd(x,y)
,然后反演:
设
f(i)
表示满足
gcd(x,y)
等于
i
的有序数对
F(i)=∑i|df(d)=⌊ni⌋⌊mi⌋
反演得:
f(1)=∑min(n/p,m/p)dμ(d)⌊n/pd⌋⌊m/pd⌋
总答案为:
∑min(n,m)p∑min(n/p,m/p)dμ(d)⌊n/pd⌋⌊m/pd⌋
现在就是要考虑如何优化求和过程了。
注意到
⌊n/pd⌋⌊m/pd⌋
这个东西是一段一段的,所以设
T=p∗d
,得
∑min(n,m)T=1⌊nT⌋⌊mT⌋∑pμ(T/p)
如果我们能求出后面这个
∑pμ(T/p)
的前缀和,之后的处理就和 这题 一样了,分块求和。
其实直接暴力枚举p就能求啦,复杂度是O(n)的。因为素数个数约为
n/lnn
, 而形如1+1/2+1/3+1/4…+1/n可以估计出来约为
lnn
, 所以均摊下来共
O(n)
。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=10000005, N=10000000;
int n,m,_test,mu[maxn],p[maxn],sum[maxn];
long long ans;
bool vis[maxn];
void get_mu(){
memset(vis,1,sizeof(vis));
mu[1]=1;
for(int i=2;i<=N;i++){
if(vis[i]) p[++p[0]]=i, mu[i]=-1;
for(int j=1;j<=p[0]&&i*p[j]<=N;j++){
vis[i*p[j]]=false;
if(i%p[j]==0){ mu[i*p[j]]=0; break; }
mu[i*p[j]]=-mu[i];
}
}
}
int main(){
freopen("bzoj2820.in","r",stdin);
freopen("bzoj2820.out","w",stdout);
get_mu();
for(int i=1;i<=p[0];i++)
for(int j=1;p[i]*j<=N;j++) sum[p[i]*j]+=mu[j];
for(int i=1;i<=N;i++) sum[i]+=sum[i-1];
scanf("%d",&_test);
while(_test--){
scanf("%d%d",&n,&m); if(n>m) swap(n,m);
ans=0;
for(int i=1,nxt=0;i<=n;i=nxt+1){
nxt=min(n/(n/i),m/(m/i));
ans+=((long long)n/i)*(m/i)*(sum[nxt]-sum[i-1]);
}
printf("%lld\n",ans);
}
}