Mophues
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 327670/327670 K (Java/Others)
Total Submission(s): 910 Accepted Submission(s): 345
Problem Description
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.
Sample Input
Sample Output
Source
2013 ACM/ICPC Asia Regional Hangzhou Online
题目:求1<=a<=n,1<=b<=m ,gcd(a,b) 的质因数个数 <= p的a,b的组合数
莫比乌斯反演:
http://blog.csdn.net/acdreamers/article/details/12871643 详情看这篇博客。写得挺详细。赞~~
如果定义最大公约数为x,
定义函数F(x) = (n/x)*(m/x)
显然最大公约数为x的a,b组合数包含在F(x)中了,但是F(x)还包括了gcd = 2*x,3*x,......,y*x的情况,需要把它们删除
实际是,我们要求得是n/x,m/x的组合中有几组是互质的情况。如果用容斥的方法,我们需要删除包含了质因数2,3,5.。。。。的情况,即减去(n/x)/2*(m/x)/2这种情况
然后因为如10,这样包含两个质因数的情况呗删除两次,又需要加回来。又要加上m/x/10 * n/x/10。
对于每个因数(不只是质因数),会发现只需要加一次,或者减一次即可。然后就是莫比乌斯函数啦。
定义Y(x)为最大公约数为x的组合数,u(x)为x的莫比乌斯函数,那么
Y(x) = F(x)*u(1) + F(x*2)*u(2) + F(x*3)+ u(3) + ............ + F(x*n)*u(n)
枚举x = 1 to n
可以算出没给F(x)的系数,再通过系数*F(x)就能得到答案了。 最后通过分块加速,可以做到n^1/2的优化
-----------------------------------------参考博客写了,我就不啰嗦了
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
#define maxn 500007
//记录质因数个数,
int prinum[maxn];
//每个数的u函数
int fu[maxn];
//记录是否为质数
int check[maxn];
//记录u函数的前缀和,第二位表示质因数个数
int usnum[maxn][20];
int call(int j,int i){
if(j % i != 0) return 0;
return call(j/i,i)+1;
}
void init(){
memset(check,0,sizeof(check));
memset(prinum,0,sizeof(prinum));
memset(usnum,0,sizeof(usnum));
memset(fu,0,sizeof(fu));
//筛出质数,并计算每个数的质因数个数
for(int i = 2;i < maxn; i++){
if(check[i]) continue;
for(int j = i;j < maxn; j += i){
check[j] = 1;
if(j % (i*i) == 0) fu[j] = -1;
prinum[j] += call(j,i);
if(fu[j] != -1) fu[j]++;
}
}
for(int i = 1;i < maxn; i++)
if(fu[i] == -1)
fu[i] = 0;
else
fu[i] = 1 - 2*(fu[i]&1);
//如筛素数法,计算每个数的U值之和
for(int i = 1;i < maxn; i++){
for(int j = i;j < maxn; j+=i){
usnum[j][prinum[i]] += fu[j/i];
}
}
//计算u的前缀和
for(int i = 1;i < maxn; i++){
for(int j = 1;j < 20; j++)
usnum[i][j] += usnum[i][j-1];
for(int j = 0;j < 20; j++)
usnum[i][j] += usnum[i-1][j];
}
}
int main(){
init();
int t,n,m,p;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&p);
if(n > m) swap(n,m);
long long ans = 0;
p = min(p,19);
int j;
if(p > 19)
ans = 1ll*n*m;
else
for(int i = 1;i <= n; i = j + 1){
j = min(n/(n/i),m/(m/i));
ans += (usnum[j][p]-usnum[i-1][p])*1ll*(n/i)*(m/i);
}
printf("%I64d\n",ans);
}
return 0;
}