1. 程式人生 > >HDU 2017 多校聯合訓練賽2 1009 6053 TrickGCD 莫比烏斯函式

HDU 2017 多校聯合訓練賽2 1009 6053 TrickGCD 莫比烏斯函式

莫比烏斯函式完整定義的通俗表達是: 1)莫比烏斯函式μ(n)的定義域是N 2)μ(1)=1 3)當n存在平方因子時,μ(n)=0 4)當n是素數或奇數個不同素數之積時,μ(n)=-1 5)當n是偶數個不同素數之積時,μ(n)=1 定義域為 [ 1, 50 ] 的莫比烏斯函式值如下:(莫比烏斯函式相關內容摘自百度百科)

根據莫比烏斯函式的定義我們可以得到,所有素數的函式值μ(n)=-1,我們可以將所有以素數為公因子的B數列的所有情況加到ans集合中,然後我們會發現公因子為非素數的情況是重複出現的。
比如,6=2*3,6在公因子為2,3時都被ans++,因此要把6減掉一次(奇加偶減);
再比如,30=2*3*5,根據奇加偶減,因數為30的排列組合結果應該加上一次,具體分析一下,30可以分解為2*3*5,因子為2,3,5時ans++,因子為2*3=6,2*5=10,3*5=15時ans--,因此因子為30時ans++(奇加偶減)。
再再比如,210=2*3*5*7,因子為2,3,5,7時ans++四次,因子為2*3=6,2*5=10,2*7=14,3*5=15,3*7=21,5*7=35時ans--六次(其實就是組合數C(4,2)=6),因子為2*3*5=30,2*3*7=42,2*5*7=70,3*5*7=105時ans++四次(即C(4,3)=4),綜上ans被加了兩次,所以因子為210時ans--。
具體容斥原理使用莫比烏斯函式實現的過程請參考程式碼。

大體的解題思路有了,觀察資料大小我們發現遍歷因子,遍歷A數列兩個for迴圈100000*100000肯定超時,所以我們在進行一步優化。
假設公因子為5,那麼比10,11,12,13,14小的5的倍數都只有兩個,5和10,那麼,我們是不是能把10~14都看做10使用呢?這樣就可以將A數列分塊操作,使用快速冪取模進行優化。

最後是我在程式碼中遇到的一些問題。
1.在最後第九十多行的兩個for迴圈中要將i,j都轉化為long long,因為迴圈裡面的運算有用到i,j,用int型別會導致精度丟失
2.num[ ]陣列要開到200000,題目資料只有100000,但是在94行的for迴圈中,雖然j*i<=100000,因為mina最大是100000,所以(j+1)*i最大也是可能超過100000的,最大能達到200000。
3.快速冪取模long long powermod(long long a,long long b),引數和返回值都要取long long,否則會精度丟失。


程式碼
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 1e9+7;
int cas, n;
int mina;
int mo[100005];//mobius函式打表
int num[200005];//數值為i的數的個數
int a[100005];

/*long long powermod(ll a, ll b)
{
    long long ans=1;
    while(b)
    {
        if(b%2==1)ans=ans*a%mod;
        a=(a*a)%mod;
        b/=2;
    }
    printf ("ans=========%d\n",ans);
    return ans;
}*/

long long int PowerMod (long long a,long long b)
{
    long long ans = 1;
    a = a%mod;
    while (b > 0)
    {
        if (b%2 == 1)
            ans = (ans*a)%mod;
        b = b/2;
        a = (a*a)%mod;
    }
    //printf ("ans=========%d\n",ans);
    return ans;
}

void mobius(int mn)
{
    mo[1] = 1;
    for(int i=1; i<=mn; i++)
    {
        for(int j=i+i; j<=mn; j+=i)
        {
            mo[j]-=mo[i];
        }
    }
}

int main()
{
    //freopen( "1009in.txt" , "r" , stdin );
	//freopen( "1009out2.txt" , "w" , stdout );
    cas = 0;
    mobius(100000);
    /*for (int i=1; i<=10000; i++)
        printf ("%d    %d  \n",i,-mo[i]);
    printf("\n");*/
    int T;
    scanf ("%d",&T);
    while (T--)
    {
        memset(num, 0, sizeof(num));
        mina = 100005;
        scanf ("%d",&n);
        for (int i=1; i<=n; ++i)
        {
            scanf ("%d",&a[i]);
            if (mina > a[i])
            {
                mina = a[i];
            }
        }
        for (int i=1; i<=n; ++i)
        {
            num[a[i]] ++;
        }
        /*for (int i=0; i<=100000; i++)
            printf ("%d ",num[i]);
        printf ("\n");*/
        for (int i=1; i<=200000; ++i)
        {
            num[i] += num[i-1];
            //printf ("%d  ",num[i]);
        }
        //printf ("\n");
        long long sum = 0;
        //printf ("mina================%d\n",mina);
        for (long long int i=2; i<=mina; ++i)//遍歷因數
        {
            long long gg = 1;
            for (long long j=1; j*i<=100000; ++j)//遍歷a序列中的所有數
            {
                gg = (gg*PowerMod(j, num[(j+1)*i-1]-num[j*i-1]))%mod;//假設i=5,求5~9,num[9]-num[5-1]
            }
            //printf ("%lld\n",mo[i]*gg);
            sum = (sum-gg*mo[i]%mod+mod)%mod;
            //printf ("s--%lld\n",sum);
        }
        printf ("Case #%d: %lld\n",++cas,sum);
    }
    return 0;
}