一类积性函数的特殊前缀和问题

Problem. 假定 \(f(n)\) 为某个积性函数且可以快速筛出前缀和,我们令 \(g(n)\) 为另一个积性函数,满足:

\[g(p^k)=f(p^k)+1 \]

现在请求出 \(g(n)\) 的前缀和。

sol.

对于 \(f\) 是完全积性函数的情形:

我们首先可以组合意义一发得到:

\[g(n)=\sum_{d\mid n}[\gcd(d,\frac nd)=1]f(d) \]

然后大力推柿子:

\[\begin{aligned} \sum_{n=1}^Ng(n)=&\sum_{n=1}^N\sum_{d\mid n}[\gcd(d,\frac nd)=1]f(d)\\ =&\sum_{d=1}^Nf(d)\sum_{k=1}^{N/d}[\gcd(d,k)=1]\\ =&\sum_{d=1}^Nf(d)\sum_{k=1}^{N/d}\sum_{t\mid d,t\mid k}\mu(t) \end{aligned} \]

最后可以得到

\[\sum_{n=1}^Ng(n)=\sum_{t=1}^\sqrt{N}\mu(t)\sum_{d=1}^{N/t^2}f(dt)\lfloor\frac{N/t^2}{d}\rfloor \]

然后使用完全积性函数的性质:

\[=\sum_{t=1}^\sqrt{N}\mu(t)f(t)\sum_{d=1}^{N/t^2}f(d)\lfloor\frac{N/t^2}{d}\rfloor \]

复杂度为:

\[\int_1^N\sqrt{\frac{N}{x^2}}\ \mathrm dx=\mathcal O(\sqrt N\log N) \]

例子:

\(g(n)\) 为积性函数,且满足:

\[g(p^k)=p^k+1 \]

\(g\) 的前缀和,\(n\leq 10^{12}\)

sol.

这就是 \(f(n)=n\) 的情况,大力爆算即可。

#include<cstdio>
#include<cmath>

typedef long long ll;
const int N = 1E+7;
const ll mod = 998244353;

ll n, ans, Mu[N + 5];
int cnt, prime[N + 5], mu[N + 5];
bool nprime[N + 5];

inline ll S(ll n) { n %= mod; return n * (n + 1) / 2 % mod; }
inline ll calc(ll n, ll k) {
	n /= k * k; ll res = 0;
	for(ll l = 1, r; l <= n; l = r + 1) {
		r = n / (n / l);
		(res += (S(r) - S(l - 1)) * (n / l)) %= mod;
	} return res;
}

void pre(ll N) {
	mu[1] = 1;
	for(ll i = 2; i <= N; ++i) {
		if(!nprime[i]) prime[++cnt] = i, mu[i] = -1;
		for(int j = 1; j <= cnt && i * prime[j] <= N; ++j) {
			nprime[i * prime[j]] = 1;
			if(i % prime[j]) mu[i * prime[j]] = -mu[i];
			else { mu[i * prime[j]] = 0; break; }
		}
	}
	
	for(int i = 1; i <= N; ++i)
		Mu[i] = (Mu[i - 1] + mu[i] * i) % mod;
}

int main() {
	scanf("%lld", &n), pre(sqrt(n) + 5);
	for(ll l = 1, r; l * l <= n; l = r + 1) {
		r = sqrt(n / (n / (l * l)));
		(ans += (Mu[r] - Mu[l - 1]) * calc(n, l)) %= mod;
	} printf("%d", (ans + mod) % mod);
}

在不是完全积性函数的情况下:

我们定义一个新的函数 \(h(n)\) 满足:

\[h(n)=\sum_{d\mid n}f(d) \]

然后假定我们有另一个函数 \(s(n)\) 满足:

\[s(n)\otimes h(n)=g(n) \]

于是注意到 \(s(p)=0\),这个可以大力 Powerful Number 筛

\[\begin{aligned} \sum_{n=1}^Ng(n)=&\sum_{i=1}^ns(i)\sum_{j=1}^{n/i}h(j)\\ =&\sum_{i=1}^ns(i)\sum_{j=1}^{n/i}\sum_{d\mid j}f(d)\\ =&\sum_{i=1}^ns(i)\sum_{d=1}^{N/i}f(d)\lfloor\frac{N/i}{d}\rfloor\\ \end{aligned} \]

于是可以快速进行计算,复杂度(绝大多数时候)是 \(\mathcal O(n^{2/3})\)

一个简单的例子:

\(g(n)\) 为积性函数,且满足:

\[g(p^k)=p^k-p^{k-1}+1 \]

\(g\) 的前缀和,\(n\leq 10^{14}\)

sol.

这就是上面的 \(f=\varphi\) 的特殊情况

这时候,通过上面的柿子可以得到:

\[h(n)=n\Rightarrow \sum_{i=1}^ns(i)\sum_{j=1}^{n/i}j \]

现在来考虑 \(s(p^k)\) 的形式:

\[g(p^k)=\sum_{i=0}^k s(p^i)p^{k-i}=pg(p^{k-1})+s(p^k) \]

那么

\[s(p^k)=g(p^k)-pg(p^{k-1})=1-p \]

于是大力计算即可,复杂度 \(\mathcal O(\sqrt n)\)

#include<cstdio>
#include<cmath>

typedef long long ll;
const int maxn = 1.1E+7 + 5;
const ll mod = 1E+9 + 7;

ll ans, n;
int prime[maxn], cnt;
bool nprime[maxn];

void pre(int N) {
	for(int i = 2; i <= N; ++i) {
		if(!nprime[i]) prime[++cnt] = i;
		for(int j = 1; j <= cnt && i * prime[j] <= N; ++j) {
			nprime[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}

inline ll S1(ll n) { n %= mod; return n * (n + 1) / 2 % mod; }
void DFS(int p, ll cur, ll H) {
	ll nowp = 1ll * prime[p] * prime[p];
	if(nowp > n / cur) return (ans += H * S1(n / cur)) %= mod, void();
	
	DFS(p + 1, cur, H);
	for( ; nowp <= n / cur; nowp *= prime[p]) {
		DFS(p + 1, cur * nowp, (1 - prime[p]) * H % mod);
		if(nowp > n / prime[p]) break;
	}
}

int main() {
	scanf("%lld", &n), pre(sqrt(n) + 500);
	DFS(1, 1, 1), printf("%lld", (ans + mod) % mod);
}

\(g(n)\) 为积性函数,且满足:

\[g(p^k)=p^{2k}-p^{2k-1}+1 \]

\(g\) 的前缀和,\(n\leq 10^{10}\)

sol.

这是上面的 \(f=id_1\cdot \varphi\) 的特殊情况,先来考虑 \(f\) 的前缀和,容易得到:

\[f\otimes id_1=id_2 \]

这个可以杜教筛 \(\mathcal O(n^{2/3})\) 计算。

再来考虑 Powerful Number 筛的部分,我们有:

\[h(p^k)=1+\sum_{i=1}^kp^i(p^i-p^{i-1})=p^2h(p^{k-1})-p+1 \]

现在来考虑 \(s(p^k)\) 的形式:

\[\begin{aligned} g(p^k)&=s(p^k)+\sum_{i=1}^k s(p^{k-i})h(p^i)\\ &=s(p^k)+\sum_{i=1}^ks(p^{k-i})(p^2h(p^{i-1})-p+1)\\ &=s(p^k)+p^2g(p^{k-1})-(p-1)\sum_{i=0}^{k-1}s(p^{i})\\ &=s(p^k)+p^2g(p^{k-1})-(p-1)S(p^{k-1}) \end{aligned} \]

于是得到

\[s(p^k)=(p-1)S(p^{k-1})+1-p^2 \]

于是大力计算即可,复杂度 \(\mathcal O(n^{2/3})\)

#include<cstdio>
#include<cmath>
#include<unordered_map>

typedef long long ll;
const int maxn = 5E+6 + 5;
const ll mod = 1E+9 + 7;
const ll inv6 = (mod + 1) / 6;

ll ans, N, n, phi[maxn];
int prime[maxn], cnt;
bool nprime[maxn];
std::unordered_map<ll, ll> M;

void pre(int N) {
	phi[1] = 1;
	for(int i = 2; i <= N; ++i) {
		if(!nprime[i]) prime[++cnt] = i, phi[i] = i - 1;
		for(int j = 1; j <= cnt && i * prime[j] <= N; ++j) {
			nprime[i * prime[j]] = 1;
			if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
			else { phi[i * prime[j]] = phi[i] * prime[j]; break; }
		}
	} for(int i = 1; i <= N; ++i)
		phi[i] = (phi[i - 1] + i * phi[i]) % mod;
}

inline ll S1(ll n) { n %= mod; return n * (n + 1) / 2; }
inline ll S2(ll n) { n %= mod; return n * (n + 1) % mod * (n * 2 + 1) % mod * inv6 % mod; }
inline ll Phi(ll n) {
	if(n <= N) return phi[n];
	else if(M.count(n)) return M[n];
	
	ll res = S2(n);
	for(ll l = 2, r; l <= n; l = r + 1) {
		r = n / (n / l);
		(res -= (S1(r) - S1(l - 1)) % mod * Phi(n / l)) %= mod;
	} return M[n] = res;
}

inline ll calc(ll n) {
	ll res = 0;
	for(ll l = 1, r; l <= n; l = r + 1) {
		r = n / (n / l);
		(res += (Phi(r) - Phi(l - 1)) * (n / l)) %= mod;
	} return res;
}

void DFS(int p, ll cur, ll H) {
	ll nowp = 1ll * prime[p] * prime[p];
	if(nowp > n / cur) return (ans += H * calc(n / cur)) %= mod, void();
	
	DFS(p + 1, cur, H); ll s, S = 1;
	for( ; nowp <= n / cur; nowp *= prime[p]) {
		s = (S * (prime[p] - 1) + 1 - prime[p] * prime[p]) % mod;
		(S += s) %= mod;
		
		DFS(p + 1, cur * nowp, s * H % mod);
		if(nowp > n / prime[p]) break;
	}
}

int main() {
	scanf("%lld", &n), pre(N = pow(n, 0.666) + 500);
	DFS(1, 1, 1), printf("%lld", (ans + mod) % mod);
}
posted @ 2020-12-11 13:08  whx1003  阅读(346)  评论(0编辑  收藏  举报