HDU 5942 Just a Math Problem 容斥 莫比乌斯反演
题意:\( g(k) = 2^{f(k)} \) ,求\( \sum_{i = 1}^{n} g(i) \),其中\( f(k)\)代表k的素因子个数。
思路:题目意思很简单,但是着重于推导和简化,这是数论题的一贯思路,其中g(k)的方程可以看出是求k的无平方因子的个数,那么题目就是求1~n的无平方因字数的和了。
首先我们可以从莫比乌斯函数入手。
从\( \mu(d) \)的性质有,当d为素数单次连积时\( \mu(d)=(-1)^k\),其余d不为1时\( \mu(d)=0\)
那么可知\( \mu^{2}(d) \)在d满足条件时一定为正值1,故g(k)可化为\( \sum_{d | i} \mu^{2}(d) \) 且$$ans = \sum_{i = 1}^{n}{\sum_{d | i} \mu^{2}(d)} $$
接下来从容斥方向考虑。其实我们直接枚举素因子连乘的k,那么由\(\mu(d)\)函数的性质,可知当存在\(k^2 | d\)时,\(\mu(k)\)是不为0的,这样就去掉了素因子次数大于等于2的d,那么式子又可化成$$ {\mu^{2}(d)}= { \sum_{k^2|d}{\mu(k)} }$$
进一步$${\sum_{k=1}^{n}\sum_{k^2 | d} {\mu(k)}\lfloor\frac{n}{d}\rfloor }$$
\(\lfloor\frac{n}{d}\rfloor\)是1~n中被d整除的个数。
其中d为k^2的整倍数。
交换求和符号,将式子化成
$$\sum_{k = 1}^{n}{\mu(k)}\sum_{i=1}^{\lfloor \frac{n}{k^2}\rfloor} \lfloor \frac{n}{k^{2} i} \rfloor$$
接着枚举倍数求和就可以了,其中后个求和函数在1e6范围内可以预处理。
/** @Date : 2016-12-04-22.09 * @Author : Lweleth (SoungEarlf@gmail.com) * @Link : https://github.com/ * @Version : */ #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 = 1e6+20; const LL mod = 1e9 + 7; const double eps = 1e-8; int sum1[N]; int pri[N]; int mu[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; } } } } int sum(LL n)// { if(n < N && sum1[n])//小于N下且已经处理了直接返回预处理的值 return sum1[n]; LL t = 0; for(LL i = 1, j = 0; i <= n; i = j + 1) { j = n / (n / i); t += n / i * (j - i + 1);//优化,大于n一半的直接加 } t %= mod; if(n >= N) return t; else return sum1[n] = t; } int main() { int T; mobius(); while(~scanf("%d", &T)) { int cnt = 0; while(T--) { LL n; LL ans = 0; scanf("%lld", &n); for(LL i = 1; i <= n / i; i++)//枚举sqrt(n)的因子 if(mu[i]) ans = (ans + sum(n/i/i) * mu[i]) % mod;//注意i取整 printf("Case #%d: %lld\n", ++cnt, (ans + mod) % mod); } } return 0; }