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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章