神犇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
#include<bits/stdc++.h>
using namespace std;
#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)
#define ll long long
/*
题目意思不解释:
正常的莫比乌斯思维就是:
sigma p(p是质数) n/(i*p)*m/(i*p)*u(i),i枚举到min(n,m)。
但还可以优化,式子可以继续化简,
详见:https://blog.csdn.net/consciousman/article/details/77888386
最后奇妙的数学体现在求前缀和的函数上,用到了贡献的思想。
*/
const int maxn =1e7+5;
const int mod=998244353;
int prim[maxn],miu[maxn],cnt=0;
int s[maxn];
bool vis[maxn];///莫比乌斯函数的题目前缀和。
void sieve(int N)
{
cnt=0;miu[1]=1;
for(int i=2;i<=N;i++)
{
if(!vis[i])
{
prim[cnt++]=i;
miu[i]=-1;
}
for(int j=0;j<cnt;j++)
{
ll k=i*prim[j];
if(k>N) break;
vis[k]=1;
if(i%prim[j]) miu[k]=-miu[i];
else break;
}
}
memset(s,0,sizeof(s));
for(int i=0;i<cnt;i++)
for(int j=prim[i],k=1;j<=N;j+=prim[i],k++)
s[j] += miu[k];///贡献的思想
for(int i=1;i<=N;i++) s[i]+=s[i-1];///求个前缀和,方便分块答案的计算
}
ll n,m;
int main()
{
sieve(1e7);
int t;scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);
if(n>m) swap(n,m);///n是最小的,枚举n
ll ans=0;
for(ll i=1,last;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(s[last]-s[i-1]);///二维分块
}
printf("%lld\n",ans);
}
return 0;
}