3月6日 莫比烏斯反演(poj 3904)

莫比烏斯反演可以加速運算,今天大致瞭解了應用,但是推導還是印象不深的。莫比烏斯反演兩種形式,第一種是約數形式:
f(n)=d|ng(d)g(n)=d|nμ(d)f(nd)
第二種是倍數形式:
f(n)=n|dg(d)g(n)=n|dμ(dn)f(d)
其中g(d) 是我們的目標函數,嚴格來說這裏d是變量。比如說下面說的這道題:
Sky Code
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 3017 Accepted: 1021
Description

Stancu likes space travels but he is a poor software developer and will never be able to buy his own spacecraft. That is why he is preparing to steal the spacecraft of Petru. There is only one problem – Petru has locked the spacecraft with a sophisticated cryptosystem based on the ID numbers of the stars from the Milky Way Galaxy. For breaking the system Stancu has to check each subset of four stars such that the only common divisor of their numbers is 1. Nasty, isn’t it? Fortunately, Stancu has succeeded to limit the number of the interesting stars to N but, any way, the possible subsets of four stars can be too many. Help him to find their number and to decide if there is a chance to break the system.
Input

In the input file several test cases are given. For each test case on the first line the number N of interesting stars is given (1 ≤ N ≤ 10000). The second line of the test case contains the list of ID numbers of the interesting stars, separated by spaces. Each ID is a positive integer which is no greater than 10000. The input data terminate with the end of file.
Output

For each test case the program should print one line with the number of subsets with the asked property.
Sample Input

4
2 3 4 5
4
2 4 6 8
7
2 3 4 5 7 6 8
Sample Output

1
0

這裏我們構造g(d)就是公因數爲d的四元數組組合個數。則f(n)就是公因數爲d,2d,3d,…的組合個數之和,這個是比較容易求得的,可以用以下方式篩:對d=i,遍歷i,2i,…(i<=max),如果讀取的數中有這個數則count[i]++,表示含有因子d的數字個數,從中取出4個數(計算組合數即可)。
那麼用第二種形式就可以解決問題了。注意莫比烏斯篩,是一個線性篩。

#include<cstdio>
#include<algorithm>
#define N 10000+3
#define ll long long
int mu[N], pri[N];
bool nopri[N];
int cnt;
void ini()
{
    mu[1] = 1;
    for (int i = 2; i < N; i++)
    {
        if (!nopri[i])
        {
            mu[i] = -1;
            pri[cnt++] = i;
            //printf("pri[%d]=%d\n", cnt - 1, i);
        }
        for (int j = 0; j < cnt&&pri[j] * i < N; j++)
        {
            nopri[pri[j] * i] = 1;
            //printf("%d\n", pri[j]);
            if (i%pri[j] == 0)
            {
                mu[i*pri[j]] = 0;
                break;
            }
            else
            {
                mu[i*pri[j]] = -mu[i];
            }
        }
    }
}
ll com(int m)
{
    return (ll)m*(m - 1)*(m - 2)*(m - 3) / 24;
}
bool data[N];//1表示讀取到了這個數
//data在main中memset
int np;
int M;
int count[N];
ll F()
{
    ll res = 0;
    for (int i = 1; i <= M; i++)
    {
        for(int j=i;j<=M;j+=i)
            if (data[j])
            {
                count[i]++;
            }
        if (count[i] >= 4)res += (ll)mu[i]*com(count[i]);
    }
    return res;
}
int main()
{
    ini();
    while (scanf("%d", &np)!= EOF)
    {
        memset(data, 0, sizeof(data));
        memset(count, 0, sizeof(count));
        M = 0;
        for (int i = 0; i < np; i++)
        {
            int tmp;
            scanf("%d", &tmp);
            data[tmp] = 1;
            if (tmp > M)M = tmp;
        }
        if (np < 4)
        {
            printf("0\n");
            continue;
        }
        printf("%lld\n", F());
    }
    return 0;
}
發佈了50 篇原創文章 · 獲贊 11 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章