1. 程式人生 > >hdu 6053 TrickGCD

hdu 6053 TrickGCD

Problem

莫比烏斯反演介紹

Reference

Meaning

給定一個序列 {An},要構造一個序列 {Bn},滿足:

  • 1BiAi
  • gcdB1Bn2

問構造的方案數。

Analysis

暴力的做法,就是列舉 gcd,每一個 gcd 產生的答案是 ni=1Aigcd,總的答案就是它們的求和,但要去重,可以用莫比烏斯反演。
…好吧其實現在並不太懂莫比烏斯反演…按官方題解,定義:
F(x):gcd 是 x 倍數的方案數
我猜相應的就有:
f(x):gcd 是 x 的方案數
於是:
F(x)=x|df(d)f

(x)=x|dμ(dx)F(d)
F(x)=ni=1Aix,就可以做了。
按 dalao 的說法,因為列舉 gcd 時是從 2 開始的,f(1) 沒有用到,所以在乘 μ(i)的時候,要多乘一個 -1,於是就有了第二個參考部落格的反演式。
在處理那個連乘式的時候,要用一點優化處理。
好吧還是很迷…

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000, A = N, MOD = 1000000007;

int
a[N], sum[N<<1|1]; int prime[A+1]; int mu[A+1]; void Mobius() { mu[1] = 1; memset(prime, -1, sizeof prime); for(int i = 2, top = 0; i <= A; ++i) { if(prime[i]) { prime[top++] = i; mu[i] = -1; } for(int j = 0; j < top && i * prime[j] <= A; ++j) { prime[i * prime[j]] = 0
; if(i % prime[j]) mu[i * prime[j]] = -mu[i]; else { mu[i * prime[j]] = 0; break; } } } } int fast_pow(long long a, int b) { long long x = 1; for( ; b; b >>= 1, a = a * a % MOD) if(b & 1) x = x * a % MOD; return x % MOD; } int main() { Mobius(); int T; scanf("%d", &T); for(int kase = 1; kase <= T; ++kase) { int n, small = A, big = 0; scanf("%d", &n); memset(sum, 0, sizeof sum); for(int i = 0; i < n; ++i) { scanf("%d", a+i); ++sum[a[i]]; small = min(small, a[i]); big = max(big, a[i]); } for(int i = 1; i <= big << 1; ++i) sum[i] += sum[i-1]; long long ans = 0, tmp; for(int d = 2; d <= small; ++d) if(tmp = -mu[d]) // mu[d] != 0 { for(int i = d; i <= big; i += d) tmp = tmp * fast_pow(i/d, sum[i+d-1]-sum[i-1]) % MOD; ans = (ans + tmp + MOD) % MOD; } printf("Case #%d: %I64d\n", kase, ans); } return 0; }