Description
神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种
傻×必然不会了,于是向你来请教……多组输入
T = 10000
N, M <= 10000000
Solution
感觉做了这么点反演题终于有点头绪了。。
首先想到枚举gcd
ans=∑d为质数 ∑i=1⌊nd⌋∑j=1⌊md⌋[1=gcd(i,j)]ans=∑d为质数 ∑i=1⌊nd⌋∑j=1⌊md⌋[1=gcd(i,j)]
然后套路一波设
f(x)=∑i=1n∑j=1m[x=gcd(i,j)]f(x)=∑i=1n∑j=1m[x=gcd(i,j)]
再设
g(x)=∑i=1n∑j=1m[x|gcd(i,j)]g(x)=∑i=1n∑j=1m[x|gcd(i,j)]
显然有
g(x)=∑x|df(d)=⌊nx⌋⌊mx⌋g(x)=∑x|df(d)=⌊nx⌋⌊mx⌋
这里是常见形式的其中一种,反演一波得
f(x)=∑x|dμ(dx)g(d)f(x)=∑x|dμ(dx)g(d)
注意到我们一开始要求1的情况,那么观察一波发现
f(1)=∑d=1nμ(d)∗⌊nd⌋⌊md⌋f(1)=∑d=1nμ(d)∗⌊nd⌋⌊md⌋
套进求ans的式子里
ans=∑d为质数 ∑x=1nμ(x)∗⌊ndx⌋⌊mdx⌋ans=∑d为质数 ∑x=1nμ(x)∗⌊ndx⌋⌊mdx⌋
然鹅发现这样还是不能过时限,考虑令T=dxT=dx,枚举T的话又有
∑T=1n⌊nT⌋⌊mT⌋∑d为质数且d|Tμ(Td)∑T=1n⌊nT⌋⌊mT⌋∑d为质数且d|Tμ(Td)
那么预处理一波满足条件的μμ然后分块即可
由于μμ的取值极小可以考虑用bool或者short之类的卡空间
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const int N=10000000;
const int M=664581;
int prime[M];
int sum[N+1];
short mu[N+1];
bool not_prime[N+1];
void pre_work() {
mu[1]=1;
rep(i,2,N) {
if (!not_prime[i]) {
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=N;j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) {
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
rep(i,1,prime[0]) {
for (int j=prime[i];j<=N;j+=prime[i]) {
sum[j]+=mu[j/prime[i]];
}
}
rep(i,1,N) sum[i]+=sum[i-1];
}
void solve(int n,int m) {
if (n>m) std:: swap(n,m);
LL ans=0;
for (int i=1,j;i<=n;i=j+1) {
j=std:: min(n/(n/i),m/(m/i));
ans+=1ll*(n/i)*(m/i)*(sum[j]-sum[i-1]);
}
printf("%lld\n", ans);
}
int main(void) {
pre_work();
int T; scanf("%d",&T);
while (T--) {
int n,m; scanf("%d%d",&n,&m);
solve(n,m);
}
return 0;
}