HDU 6053 TrickGCD 莫比乌斯函数/容斥/筛法
题意:给出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 (SoungEarlf@gmail.com) * @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; }