P3957YY的GCD
问题描述
神犇YY虐完数论后给傻×kAc出了一题
给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对
kAc这种傻×必然不会了,于是向你来请教……
输入格式
第一行一个整数T 表述数据组数
接下来T行,每行两个正整数,表示N, M
输出格式
T行,每行一个整数表示第i组数据的结果
样例输入
2
10 10
100 100
样例输出
30
2791
提示
T = 10000
N, M <= 10000000
显然Ans=∑min(n,m)p∑ni=1∑mj=1[gcd(i,j)==p],p为质数Ans=∑pmin(n,m)∑i=1n∑j=1m[gcd(i,j)==p],p为质数
令f(p)=∑i=1n∑j=1m[gcd(i,j)==p],F(p)=∑i=1n∑j=1m[p|gcd(i,j)]令f(p)=∑i=1n∑j=1m[gcd(i,j)==p],F(p)=∑i=1n∑j=1m[p|gcd(i,j)]
令N=min(n,m),那么F(p)=∑p|kNf(k)=⌊np⌋⌊mp⌋令N=min(n,m),那么F(p)=∑p|kNf(k)=⌊np⌋⌊mp⌋
反演一下,f(p)=∑p|kNμ(kp)F(k)=∑p|kNμ(kp)⌊nk⌋⌊mk⌋反演一下,f(p)=∑p|kNμ(kp)F(k)=∑p|kNμ(kp)⌊nk⌋⌊mk⌋
那么Ans=∑pNf(p)=∑pN∑p|kNμ(kp)⌊nk⌋⌊mk⌋那么Ans=∑pNf(p)=∑pN∑p|kNμ(kp)⌊nk⌋⌊mk⌋
如果是单组询问,到这里已经可以了,但本题有多组询问,因此
交换一下枚举顺序,Ans=∑k=1N⌊nk⌋⌊mk⌋∑p|kμ(kp),p为质数交换一下枚举顺序,Ans=∑k=1N⌊nk⌋⌊mk⌋∑p|kμ(kp),p为质数
后面的部分可以直接枚举质数再枚举倍数预处理,复杂度接近O(n)O(n)
但显然可以线性筛处理,令其为g[i]g[i],当p[j]|ip[j]|i时,g[i∗p[j]]=μ(i)g[i∗p[j]]=μ(i),否则g[i∗p[j]]=mu[i]−g[i]g[i∗p[j]]=mu[i]−g[i]
然后分块处理即可,复杂度O(n+Tn−−√)O(n+Tn)
代码:
#include<stdio.h>
#include<algorithm>
#define N 10000005
#define ll long long
using namespace std;
ll T,P[N],tot,mu[N],g[N];
bool mark[N];
void EU()
{
ll i,j;mu[1]=1;g[1]=0;
for(i=2;i<N;i++)
{
if(!mark[i])P[++tot]=i,mu[i]=-1,g[i]=1;
for(j=1;j<=tot&&i*P[j]<N;j++)
if(i%P[j])mu[i*P[j]]=-mu[i],g[i*P[j]]=mu[i]-g[i],mark[i*P[j]]=1;
else {mu[i*P[j]]=0;g[i*P[j]]=mu[i];mark[i*P[j]]=1;break;}
}
for(i=2;i<N;i++)g[i]+=g[i-1];
}
int main()
{
ll n,m,i,j,ans;
scanf("%lld",&T);EU();
while(T--)
{
scanf("%lld%lld",&n,&m);ans=0;
for(i=1;i<=n&&i<=m;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(g[j]-g[i-1]);
}
printf("%lld\n",ans);
}
}