1. 程式人生 > >【HDU 6053 TrickGCD】 + 莫比烏斯反演

【HDU 6053 TrickGCD】 + 莫比烏斯反演

TrickGCD

Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1002 Accepted Submission(s): 389

Problem Description
You are given an array A , and Zhu wants to know there are how many different array B satisfy the following conditions?

  • 1≤Bi≤Ai
  • For each pair( l , r ) (1≤l≤r≤n) , gcd(bl,bl+1…br)≥2

Input
The first line is an integer T(1≤T≤10) describe the number of test cases.

Each test case begins with an integer number n describe the size of array A.

Then a line contains n numbers describe each element of A

You can assume that 1≤n,Ai≤105

Output
For the kth test case , first output “Case #k: ” , then output an integer as answer in a single line . because the answer may be large , so you are only need to output answer mod 109+7

Sample Input
1
4
4 4 4 4

Sample Output
Case #1: 17

Source
2017 Multi-University Training Contest - Team 2

題意 : b[i] 可以從 1 ~ a[i] 之間選個數,任意 gcd(b[l] ~ b[r]) > 1,可以有多少中 b 陣列

思路 : 列舉每個因數 o,對於 b,可以選擇的數有 o * 1,o * 2,o * 3…o * b / o,有 b / o 個,對整個陣列來說 貢獻所有 b / o 的乘積, 預處理一下 l ~ r 之間有多少數,可以算出 o * r ~ o * (r + 1) - 1 之間的數的貢獻都為 r,快速冪一下,最後利用 莫比烏斯反演去重

莫比烏斯反演是數論中的重要內容,在許多情況下能夠簡化運算
我們需要找到f(n)與F(n)之間的關係。從和函式定義當中,我們可以知道:
F(1)=f(1)
F(2)=f(1)+f(2)
F(3)=f(1)+ f(3)
F(4)=f(1)+f(2)+f(4)
F(5)=f(1)+f(5)
F(6)=f(1)+f(2)+f(3)+f(6)
F(7)=f(1)+f(7)
F(8)=f(1)+f(2)+f(4)+f(8)
那麼:
f(1)=F(1)
f(2)=F(2)-f(1)=F(2)-F(1)
f(3) =F(3)-F(1)
f(4)=F(4) -f(2)- f(1) =F(4)-F(2)
f(5) =F(5)-F(1)
f(6)=F(6)-F(3)-F(2)+F(1)
f(7)=F(7)-F(1)
f(8)=F(8)-F(4)
從中,可以看出,若n=p2(p為質數)那麼,F(p)=f(1)+f(p),F(n)=f(1)+f(p)+f(p2),所以,f(n)=F(p2)-F(p)

AC程式碼:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX = 1e5 + 10;
const int mod = 1e9 + 7;
typedef long long LL;
LL sum[MAX],num[MAX];
LL km(LL a,LL b){
    LL ans = 1;
    while(b){
        if(b & 1) ans = (ans * a) % mod;
        b /= 2; a = (a * a) % mod;
    }
    return ans;
}
int main()
{
    int T,nl = 0,n,x;
    scanf("%d",&T);
    while(T--){
        memset(num,0,sizeof(num));
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
            scanf("%d",&x),num[x]++;
        for(int i = 2; i < MAX; i++) num[i] += num[i - 1];
        LL ans = 0,cut = 0;
        for(int i = 2; i < MAX; i++){
            if(num[i - 1]) continue; // 有不滿足的點
            sum[i] = 1;
            for(int j = i; j < MAX; j += i){
                if(j + i >= MAX) cut = num[MAX - 1] - num[j - 1]; // j ~ MAX
                else cut = num[j + i - 1] - num[j - 1];
                if(!cut) continue;
                sum[i] = (sum[i] * km(j / i,cut)) % mod;
            }
        }
        for(int i = MAX - 1; i >= 2; i--)
            for(int j = i + i; j < MAX; j += i)
                sum[i] = (sum[i] - sum[j] + mod) % mod;
        for(int i = 2; i < MAX; i++) ans = (ans + sum[i]) % mod;
        printf("Case #%d: %lld\n",++nl,ans);
    }
    return 0;
}