[51nod 1237] 最大公約數之和 V3(莫比烏斯反演+杜教篩)

題意

  • ans=i=1nj=1n(i,j)(n1010)ans=\displaystyle\sum_{i = 1}^n\sum_{j = 1}^n(i,j),(n\le10^{10})

首先是套路的化式子爲枚舉gcd:
ans=d=1ndi=1nj=1n[(i,j)=d]ans=\sum_{d = 1}^nd\sum_{i = 1}^n\sum_{j = 1}^n[(i,j)=d]

我們設f(d)=i=1nj=1n[(i,j)=d]F(n)=ndf(d)f(d)=\sum_{i = 1}^n\sum_{j = 1}^n[(i,j)=d],F(n)=\sum_{n|d}f(d),莫比烏斯反演可得:
f(n)=ndμ(dn)F(d)f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)
又因爲F(n)F(n)很好算,等於nd2\lfloor\frac{n}{d}\rfloor^2,那麼代入原式:
ans=d=1nd(dkμ(kd)nk2)ans=\sum_{d = 1}^nd(\sum_{d|k}\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor^2)
我們想把那個nd2\lfloor\frac{n}{d}\rfloor^2移出來,因爲這個東西可以整除分塊算,那麼我們改枚舉倍數爲枚舉約數:
ans=k=1nnd2(dkdμ(kd))ans=\sum_{k = 1}^n\lfloor\frac{n}{d}\rfloor^2(\sum_{d|k}d\mu(\frac{k}{d}))
我們想起來莫比烏斯函數有一個性質:
ϕ(n)n=dnμ(d)d\frac{\phi(n)}{n}=\sum_{d|n}\frac{\mu(d)}{d}
兩邊同時乘上nn,那麼:
ϕ(n)=dnndμ(d) \phi(n)=\sum_{d | n}\frac{n}{d}\mu(d)
我們發現這個式子和答案後面那個式子是一樣的,那麼:
ans=k=1nnd2ϕ(n) ans=\sum_{k = 1}^n\lfloor\frac{n}{d}\rfloor^2\phi(n)
我們用杜教篩算歐拉函數的前綴和整除分塊做就好了,複雜度O(n23)O(n^{\frac{2}{3}})

#include <bits/stdc++.h>

#define pb push_back
#define ll long long
#define For(i, a, b) for (int i = a; i <= b; ++ i)

using namespace std;

const int N = 2e6;
const int mod = 1e9 + 7;

vector<int> Prime;
bitset<N + 3> Notp;
unordered_map<ll, int> SPhi;

int Phi[N + 3], Sphi[N + 3];

int Mul(int x, int y) { return 1ll * x * y % mod; }
int Add(int x, int y) { return (x += y) < mod ? x : x - mod; }

void Get_Prime() {
	Notp[0] = Notp[1] = Phi[1] = 1; 
	For(i, 2, N) { 
		if (!Notp[i]) Prime.pb(i), Phi[i] = i - 1;
		for (auto P : Prime) {
			if (P * i > N) break;
			Notp[P * i] = 1; 
			if (!(i % P)) {
				Phi[P * i] = Mul(Phi[i], P);
				break;
			}
			Phi[P * i] = Mul(Phi[i], P - 1);
		}
	}
	For(i, 1, N) Sphi[i] = Add(Sphi[i - 1], Phi[i]);
}

int Query_Phi(ll n) {
	if (n <= N) return Sphi[n];
	if (SPhi[n]) return SPhi[n];
	int res = ((n % mod) * (n % mod + 1)) % mod * (mod + 1) / 2 % mod;
	for (ll l = 2, r; l <= n; l = r + 1) {
		r = n / (n / l);
		res = Add(res, mod - 1ll * Query_Phi(n / l) * (r - l + 1) % mod);
	}
	return SPhi[n] = res;
}

int main() {

	ll n, ans = 0;

	Get_Prime();

	cin >> n;
	for (ll l = 1, r; l <= n; l = r + 1) {
		r = n / (n / l);
		ans += 1ll * Mul(n / l % mod, n / l % mod) * (Query_Phi(r) - Query_Phi(l - 1)) % mod;
	}
	printf("%lld\n", (ans % mod + mod) % mod);

	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章