C - Mophues
As we know, any positive integer C ( C >= 2 ) can be written as the multiply of some prime numbers:
C = p1×p2× p3× ... × pk
which p1, p2 ... pk are all prime numbers.For example, if C = 24, then:
24 = 2 × 2 × 2 × 3
here, p1 = p2 = p3 = 2, p4 = 3, k = 4
Given two integers P and C. if k<=P( k is the number of C's prime factors), we call C a lucky number of P.
Now, XXX needs to count the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of a given P ( "gcd" means "greatest common divisor").
Please note that we define 1 as lucky number of any non-negative integers because 1 has no prime factor.
Input
The first line of input is an integer Q meaning that there are Q test cases.
Then Q lines follow, each line is a test case and each test case contains three non-negative numbers: n, m and P (n, m, P <= 5×10 5. Q <=5000).
Output
For each test case, print the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of P.
题意:在1~n里面取i,在1~m里面取j,求gcd(i,j)的素因子个数小于等于p的i,j有多少对。
题解:设f(x)为在1~n里面取i,在1~m里面取j,gcd(i,j)等于x的i,j对数,F(x)为在1~n里面取i,在1~m里面取j,gcd(i,j)等于x的倍数的i,j对数。
F(x)=f(x)+f(2*x)+f(3*x)....f(k*x),其中(k*x)<min(n,m),(k+1)*x>min(n,m)
而F(x)=(n/x)*(m/x) ,其中/为向下取整
根据莫比乌斯反演可以得到:
f(x)=mu(1)*F(x)+mu(2)*F(2*x)+...mu(k)*F(k*x)
而我们要求的是所有素因子少于p的x的f(x)之和,我们可以在初始化莫比乌斯函数mu的欧拉筛里面顺便筛一下num(x),
num(x)指x的素因子个数,所以现在就是求num(x)小于p的f(x)的和,直接写肯定是t的,接下来就是优化
首先对于F(x)=(n/x)*(m/x),可以用整除分块法优化一下,优化就要求一下mu的前缀和sum,
对于f(x)=mu(1)*F(x)+mu(2)*F(2*x)+...mu(k)*F(k*x)来说:
sum[x][num(x)]=mu(1), sum[2*x][num(x)]=sum[x][num(x)]+mu(2)依次类推,
而我们需要的是所有num(x)小于等于p的x,就比如说当p等于1时,x可以取2,3等等
f(2)=mu(1)*F(2)+mu(2)*F(4)+mu(3)*F(6)+...+mu(k)*F(k*2)
f(3)=mu(1)*F(3)+mu(2)*F(6)+...mu(k)*F(k*3)
这里的F(6)的系数在f(2)里面是mu(3),在f(3)里面是mu(3),而我们求的是f(2)+f(3),因为考虑时间问题,我们不可能遍历一遍1~n,取里面的素因子个数小于等于p的x的f(x)之和,我们可以通过整除分块+前缀和的方法来遍历F(1~n),因为num(2)=num(3),所以sum[3][num[3]]+=sum[2][num[2]],所以sum[x][y]的意思其实就是num(i)等于y的f(i)的和里面的F(1)到F(x)的系数和,当你要求p等于1的F(6)的系数时,就是sum[6][1]-sum[5][1],下面是代码,题解看不懂可以结合代码一起看
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 500010
#define ll long long
int mu[maxn],vis[maxn],prime[maxn],num[maxn];
ll sum[maxn][20];
void inite(){
mu[1]=1;
num[1]=0;
int cnt=0;
for(int i=2;i<maxn;i++){
if(vis[i]==0){
mu[i]=-1;
prime[++cnt]=i;
num[i]=1;
}
for(int j=1;j<=cnt&&i*prime[j]<maxn;j++){
num[i*prime[j]]=num[i]+1;
vis[i*prime[j]]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
else{
mu[i*prime[j]]=-mu[i];
}
}
}
}
void getsum(){
for(int i=1;i<maxn;i++){
for(int j=i;j<maxn;j=j+i){
sum[j][num[i]]+=(ll)mu[j/i];
}
}
for(int i=1;i<maxn;i++){
for(int j=0;j<19;j++){
sum[i][j]+=sum[i-1][j];
}
}
for(int i=1;i<maxn;i++){
for(int j=0;j<19;j++){
sum[i][j]+=sum[i][j-1];
}
}
}
ll solve(ll n,ll m,int k){
ll r;
ll ans=0;
for(ll i=1;i<=n;i=r+1){
r=min(n/(n/i),m/(m/i));
ans=ans+(sum[r][k]-sum[i-1][k])*(m/i)*(n/i);
}
return ans;
}
int main(){
int t;
inite();
getsum();
scanf("%d",&t);
while(t--){
ll n,m,k;
scanf("%lld %lld %lld",&n,&m,&k);
if(n>m){
swap(n,m);
}
if(k>=19){
printf("%lld\n",n*m);
}
else{
printf("%lld\n",solve(n,m,k));
}
}
}