1. 程式人生 > 實用技巧 >【18徐州網路賽D】Easy Math

【18徐州網路賽D】Easy Math

補充一點性質

\[\sum_{d|n}\varphi(d) = n \]

證明

對於 \(n = 1\) ,不難驗證滿足題意

對於 \(n = p^a\) ,

\[\sum_{d|n}\varphi(d) = 1 + \sum_{i = 1}^a\varphi(p^i) \\=p^a = n \]

對於 \(n = p_1^{a_1}...p_k^{a_k}\)

\[\sum_{d|n}\varphi(d) = \sum_{i=0}^{a_1}\varphi(p_1^i)\sum_{i=0}^{a_2}\varphi(p_2^i)...\sum_{i=0}^{a_k}\varphi(p_k^i)\\=p_1^{a_1}...p_k^{a_k} = n \]

  • i % p == 0\(\varphi(i*p) = \varphi(i) * p\) , \(p\) 是質數

D. Easy Math

題意

給定 \(m,n\)

\[\sum_{i=1}^m\mu(i * n) \]

這樣就可以進行遞迴了,但是我不會算複雜度...

還注意到一個地方,線篩的判斷 i % prime[j] == 0 的地方要注意函式的取值

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;
typedef long long ll;
ll prime[N], cnt, vis[N], mu[N], sum[N];


void init() {
	mu[1] = 1;
	for (int i = 2; i < N; i++) {
		if (!vis[i]) {
			prime[++cnt] = i;
			mu[i] = -1;
		}
		for (int j = 1; j <= cnt and prime[j] * i < N; j++) {
			vis[prime[j] * i] = 1;
			mu[prime[j] * i] = -mu[i];
			if (i % prime[j] == 0) {
                //**這一行不能去掉**
				mu[i * prime[j]] = 0;
				break;
			}
		}
	}
	for (int i = 1; i < N; i++) sum[i] = sum[i - 1] + mu[i];
}

ll n, m;
unordered_map<ll, ll> M;
ll djs(ll n) {
	if (n < N) return sum[n];
	if (M.count(n)) return M[n];
	ll ans = 1;
	for (ll l = 2, r; l <= n; l = r + 1) {
		r = n / (n / l);
		ans -= (r - l + 1) * djs(n / l);
	}
	return M[n] = ans;
}
ll cal(ll n) {
	int ans = 1;
	if (n < N) return mu[n];
	for (ll i = 2; i * i <= n;i++) {
		if (n % i == 0) {
			if (n % (i * i) == 0)
				return 0;
			ans *= -1;
			n /= i;
		}
	}
	if (n > 1)
		ans *= -1;
	return ans;
}
ll solve(ll m, ll n) {
	if (n == 1) return djs(m);

	ll v = cal(n);
	if (v == 0) return 0;

	ll ans = 0;
	ll t = m < sqrt(n) ? m : sqrt(n);
	for (ll d = 1; d <= t; d++) {
		if (n % d == 0) {
			ans += cal(d) * solve(m / d, d);
			if (n / d <= m)
				ans += cal(n / d) * solve(m / (n / d), n / d);
		}
	}
	return v * ans;
}
int main() {
	init();
	scanf("%lld%lld", &m, &n);
	printf("%lld\n", solve(m, n));
}