hdu 4746 Mophues 莫比乌斯反演

来源:互联网 发布:sql server复制表结构 编辑:程序博客网 时间:2024/06/05 08:23

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×105. 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
210 10 010 10 1
 

Sample Output
6393
 

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









0 0
原创粉丝点击