LG P3653 小清新數學題
阿新 • • 發佈:2021-07-07
\(\text{Poblem}\)
求 \(\sum_{i=l}^r \mu(i)\)
\(1 \le l,r \le 10^{18}, r - l \le 10^5\)
\(\text{Analysis}\)
我們做過 \(r,l \le 10^{12}\) 次方的區間篩積性函式
但這是因為 \(\sqrt r\) 內的素數可以快速篩出來
又可以用這些素數處理 \(r \le 10^{12}\) 的數的積性函式
但現在 \(\sqrt r\) 已經達到 \(10^9\) 級別了
\(so...\)
我們仍然用 \(10^6\) 以內的素數處理,並將原來的數除掉這些素數
那麼剩下的數 \(x\) 只有三種形式
\(3.x=p^2\)
\(2.x=p\)
\(1.x=pq\)
\(p,q \in \mathbb P,p \not = q\)
考慮第一種情況,只需要判斷 \(\sqrt x\) 強轉整型後再平方是否等於 \(x\)
考慮第二種情況,由於 \(x\) 可能非常大,那麼就需要 \(Miller_Rabin\) 來判斷素數
考慮第三種情況,兩個不等的素數,\(\mu\) 不變,且判完前兩個情況只剩這種情況,不必再考慮
愉快解決
\(\text{Code}\)
#include<cstdio> #include<algorithm> #include<ctime> #include<cmath> using namespace std; typedef long long LL; const int R = 100005; LL l, r, num[R]; int prime[600005], totp, mu[R], vis[1000005]; inline LL sqr(LL x){return x * x;} inline void getprime() { for(int i = 2; i <= 1e6; i++) { if (!vis[i]) prime[++totp] = i; for(int j = 1; j <= totp && i * prime[j] <= 1e6; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } inline LL fmul(LL x, LL y, LL p) { return (x * y - (LL)((long double)x / p * y) * p + p) % p; } inline LL fpow(LL x, LL y, LL p) { LL res = 1; for(; y; y >>= 1) { if (y & 1) res = fmul(res, x, p); x = fmul(x, x, p); } return res; } inline int Miller_Rabin(LL p) { srand(time(0)); if (p <= 3) return (p == 2 || p == 3); if (!(p & 1)) return 0; LL d = p - 1, b = 0; while (!(d & 1)) d >>= 1, b++; for(int i = 0; i < 3; i++) { LL a = rand() % (p - 3) + 2, u = fpow(a, d, p), v; for(int j = 0; j < b; j++) { v = fmul(u, u, p); if ((v == 1) && (u != 1) && (u != p - 1)) return 0; u = v; } if (u != 1) return 0; } return 1; } inline LL solve() { for(int i = 1; i <= r - l + 1; i++) mu[i] = 1, num[i] = l + i - 1; for(int i = 1; i <= totp && prime[i] <= r; i++) for(LL j = max(1LL, l / prime[i]); j * prime[i] <= r; j++) if (j * prime[i] >= l) { if (j % prime[i] == 0) mu[j * prime[i] - l + 1] = 0; else mu[j * prime[i] - l + 1] *= -1, num[j * prime[i] - l + 1] /= prime[i]; } for(int i = 1; i <= r - l + 1; i++) { if (!mu[i] || num[i] == 1) continue; if (sqr(sqrt(num[i])) == num[i]) mu[i] = 0; else if (Miller_Rabin(num[i])) mu[i] *= -1; } LL ans = 0; for(int i = 1; i <= r - l + 1; i++) ans += mu[i]; return ans; } int main() { scanf("%lld%lld", &l, &r); getprime(); printf("%lld\n", solve()); }