1. 程式人生 > >HDU 6053 TrickGCD(莫比烏斯函式)

HDU 6053 TrickGCD(莫比烏斯函式)

題意:給出陣列 AA ,問有多個種 BB 陣列滿足所給條件。
思路
設 gcd(b1,...bn) = k (k >= 2),此時 k 對答案的貢獻為 (a1/k)*(a2/k)*(a3/k)*...*(an/k)
	根據容斥原理,ans = +[k=一個素數之積 時對答案的貢獻]
						-[k=兩個素數之積 時對答案的貢獻]
						+[k=三個素數之積 時對答案的貢獻]
						...
這麼去重要考慮很多,所以使用莫比烏斯函式去重,雖然我不知道莫比烏斯函式怎麼來的,我知道這是一個定理,去重的時候異常方便。 莫比烏斯函式完整定義的通俗表達: 1)莫比烏斯函式μ(n)的定義域是N 2)μ(1)=1 3)當n存在平方因子時,μ(n)=0 4)當n是素數或奇數個不同素數之積時,μ(n)=-1 5)當n是偶數個不同素數之積時,μ(n)=1
對於每個k,對sum[]進行埃式篩法的分塊,即根據k的倍數分塊
	此時每個k的貢獻 = 1^(sum[2k-1]-sum[k-1]) * 2^(sum[3k-1]-sum[2k-1]) * 3^(sum[4k-1]-sum[3k-1]) ...
	就做到 O(n(logn)^2)

如果暴力肯定會超時,所以用到劃區的方法做,比如3,4,5除以3結果都是1,小數點後的都去除,所以除數是3的時候可以把3,4,5劃分在一起,一起用快速冪計算。
#include "stdio.h"
#include "string.h"
#include <iostream>
#include <queue>
#include <math.h>
#include <algorithm>
using namespace std;
const int maxn=200000+10;
const int mod=1000000000+7;
int mu[maxn];
void init()
{
    mu[1]=1;
    for(int i=1; i<maxn; i++)
        for(int j=i+i; j<maxn; j+=i)
            mu[j]-=mu[i];
}
long long mul(long long a,int b)
{
    long long ans=1;
    while(b>0)
    {
        if(b&1){ans*=a;ans%=mod;}
        a=a*a;
        a%=mod;
        b/=2;
    }
    return ans;
}
int main()
{
    memset(mu,0,sizeof(mu));
 init();
 int t,cnt=0;
 int sum[maxn];
scanf("%d",&t);
while(t--)
{
    int n,x,min1,max1;
    memset(sum,0,sizeof(sum));
    scanf("%d",&n);
    min1=200000;
    max1=-1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        sum[x]++;
        min1=min(x,min1);
        max1=max(max1,x);
    }
    for(int i=1;i<=2*max1;i++)
        sum[i]+=sum[i-1];
    long long res=0;
    for(int i=2;i<=min1;i++)
    {
        long long temp=1;
        if(mu[i])
        {
            for(int j=i;j<=max1;j+=i)
            {
                temp*=mul(j/i,sum[j-1+i]-sum[j-1]);
                temp%=mod;
            }
            res=(res-temp*mu[i]+mod)%mod;//莫比烏斯函式偶加奇減,所以減去這個函式。

        }
    }
    printf("Case #%d: %lld\n",++cnt,res);
}
return 0;
}