gcd(i,j)的素因子个数小于等于p的i,j对数

C - Mophues

 HDU - 4746

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));
		}
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值