HDU 4746 Mophues 莫比乌斯第三弹

题意:1<=x<=n,1<=y<=m,使得gcd(x,y)=k,k的素因数个数小于等于p

例:24=2*2*2*3,k=4

解:设f[n]为gcd(a,b)=n的对数

      F[d]为d|gcd(a,b)的对数

    f[n]=sigema(mu[i],F[i*n]):

    f[1]=mu[1]*F[1]+mu[2]*F[1*2]+...+mu[n]*F[1*n]

    f[2]=mu[2]*F[2]+mu[2]*F[2*2]+...+mu[n]*F[2*n]

    ......

    sum=f[1]+f[2]+...+f[n]=G[1]*F[1]+G[2]*F[2]+...+G[n]*F[n]

    枚举每一个i,则i的倍数j为G[j]提供了mu[j/i]的贡献,即G[j]+=mu[j/i]

    因为所求为k的素因数个数,所以将G[j]开成二维数组G[j][p]表示j对素因数个数为p的贡献

    需要使用分块加速的方法,否则还是要爆炸

#include <stdio.h>
#include <string.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)<(b)?(b):(a))
#define ll __int64
const int maxn=500005;
int num[maxn];
int prime[maxn];
int mu[maxn];
int factor[maxn];
int mbs[maxn][20];
void mobius()
{
    memset(num,0,sizeof(num));
    int all=0;
    mu[1]=1;
    factor[1]=0;
    for(int i=2;i<maxn;i++)
    {
        if(!num[i])
        {
            prime[all++]=i;
            mu[i]=-1;
            factor[i]=1;                 //记录素因数个数
        }
        for(int j=0;j<all&&i*prime[j]<maxn;j++)
        {
            num[i*prime[j]]=1;
            factor[i*prime[j]]=factor[i]+1;
            if(i%prime[j])
            {
                mu[i*prime[j]]=-mu[i];
            }
            else
            {
                mu[i*prime[j]]=0;
                break;
            }
        }
    }
    return ;
}
void inti()
{
    memset(mbs,0,sizeof(mbs));
    for(int i=1;i<maxn;i++)                  
        for(int j=i;j<maxn;j+=i)
            mbs[j][factor[i]]+=mu[j/i];        //每个j在factor[i]个素因数中的贡献
/*下面是为了分块加速求和*/
    for(int i=1;i<maxn;i++)
        for(int j=0;j<19;j++)
            mbs[i][j]+=mbs[i-1][j];
    for(int i=0;i<maxn;i++)
        for(int j=1;j<19;j++)
            mbs[i][j]+=mbs[i][j-1];
    return ;
}
int main()
{
    int t,n,m,p;
    mobius();
    inti();
    ll sum;
    while(scanf("%d",&t)!=-1)
    {
        while(t--)
        {
            scanf("%d%d%d",&n,&m,&p);
            if(p>=19)                    //因为2^19>500000,所以超过了19就是全体均满足要求
            {
                printf("%I64d\n",(ll)n*m);
                continue;
            }
            if(n>m)
            {
                int te=n;
                n=m;
                m=te;
            }
            sum=0;
            for(int i=1,last;i<n;i=last+1)
            {
                last=MIN(n/(n/i),m/(m/i));           
//分块加速,因为[n/i][m/i]在i递加过程中具有重复部分,跳掉这些i可是简化计算,是复杂度降低至sqrt(n)
                sum+=((ll)(n/i)*(m/i)*(mbs[last][p]-mbs[i-1][p]));
            }
            printf("%I64d\n",sum);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值