HDU 6053 TrickGCD 莫比烏斯函數/容斥/篩法
阿新 • • 發佈:2017-07-29
mem define brush double 容斥 sum main cas sca
題意:給出n個數$a[i]$,每個數可以變成不大於它的數,現問所有數的gcd大於1的方案數。其中$(n,a[i]<=1e5)$
思路:鑒於a[i]不大,可以想到枚舉gcd的值。考慮一個$gcd(a_1,a_2,a_3…a_n)=d$,顯然每個$a_i$的倍數都滿足,有$\frac{a_i}{d}$種方案
那麽一個d對答案的貢獻為\[\prod_{i=1}^{min(a)}{\lfloor\frac{a_i}{d}\rfloor} \]
但是所有的d計入會有重復情況,考慮容斥,對d進行素數分解,發現重復情況就是d的互異素數個數為偶數的,或是素數的指數大於1的。
然後會發現這情況的系數和莫比烏斯函數定義很像$(-f(d)) = \mu(d)=(-1)^k, d={p_1}{p_2}…{p_k}$,$\mu(d) = 0,d為非1整數$
現在我們就要考慮如何快速求得一個d的貢獻了,類似篩法的思想,由於a[i]不大,預處理出前綴和,sum[i]代表小於i的數的個數,將按方案數大小分塊,累加$cnt^{sum[(cnt+1)*d-1]-sum[cnt*d-1]}$得到結果,乘上容斥系數即可。
\[ ans = \sum_{d=1}^{min(a)}{(-\mu(d)) \sum_{j=1}^{\lfloor \frac{min(a)}{d} \rfloor}{j^{\sum_{i=1}^{n}{[\lfloor \frac{a_i}{d} \rfloor = j]}} }} \]
也就是把上式j的指數前綴和優化了一下。
分塊優化也是很常見的,和以前的數論組合題已經算簡單了,我好鶸鶸鶸鶸鶸鶸阿
/** @Date : 2017-07-28 19:30:46 * @FileName: HDU 6053 莫比烏斯函數 容斥 1009.cpp * @Platform: Windows * @Author : Lweleth ([email protected]) * @Link : https://github.com/ * @Version : $Id$ */ #include <bits/stdc++.h> #define LL long long #define PII pair #define MP(x, y) make_pair((x),(y)) #define fi first #define se second #define PB(x) push_back((x)) #define MMG(x) memset((x), -1,sizeof(x)) #define MMF(x) memset((x),0,sizeof(x)) #define MMI(x) memset((x), INF, sizeof(x)) using namespace std; const int INF = 0x3f3f3f3f; const int N = 1e5+20; const double eps = 1e-8; const LL mod = 1e9 + 7; LL pri[N]; LL mu[N]; LL sum[N]; bool vis[N]; int c = 0; void mobius() { MMF(vis); mu[1] = 1; for(int i = 2; i < N; i++) { if(!vis[i]) pri[c++] = i, mu[i] = -1; for(int j = 0; j < c && i * pri[j] < N; j++) { vis[i * pri[j]] = 1; if(i % pri[j]) mu[i * pri[j]] = -mu[i]; else { mu[i * pri[j]] = 0; break; } } } for(int i = 0; i < N; i++) mu[i] *= -1; } LL fpow(LL x, LL n) { LL ans = 1; while(n > 0) { if(n & 1) ans = ans * x % mod; x = x * x % mod; n >>= 1; } return ans; } int n; LL a[N]; int main() { int icase = 0; int T; cin >> T; mobius(); while(T--) { MMF(sum); scanf("%d", &n); LL mi = INF; LL ma = -1; for(int i = 0; i < n; i++) { scanf("%lld", a + i); mi = min(a[i], mi); ma = max(a[i], ma); sum[a[i]]++; } for(int i = 2; i <= ma; i++) sum[i] += sum[i - 1]; LL ans = 0; for(LL g = 2; g <= mi; g++) { if(mu[g] == 0) continue; LL t = 1; LL cnt = 1; while(true) { int l = min(cnt * g, ma); int r = min((cnt + 1) * g - 1, ma); if(r > l - 1) t = (t * fpow(cnt, sum[r] - sum[l - 1]) % mod + mod) % mod; if(r == ma) break; cnt++; } //cout << t <<endl; ans = (ans + mu[g] * t + mod) % mod; } while(ans < 0) ans += mod; printf("Case #%d: %lld\n", ++icase, ans); } return 0; }
HDU 6053 TrickGCD 莫比烏斯函數/容斥/篩法