Заявление
Учитывая значение N, вам нужно будет найти значение G. Значение G дано в следующем коде.
G = 0; for (i = 1; i < N; i++) for (j = i+1; j <= N; j++) G += gcd(i, j);
Здесь gcd() — это функция, которая находит наибольший общий делитель двух входных чисел.
Первое решение
По функции Мёбиуса:
Временная сложность: O(Q nlog(n))
Оптимизация 1
Определите набор возможных значений N/k:
Итак, можно перебрать S:
Оптимизация 2
Создайте массив с накоплением суммы функции Мебиуса:
Теперь можно перебирать возможные значения (N/k)/d, аналогично оптимизации 1, создавать все возможные значения S[i]/d, перебирать их и умножать на накопленную сумму мёбиуса.
Временная сложность: O(Q n)
#include <bits/stdc++.h> using namespace std; #define N 1000005 using ll = long long; #define FIFO ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0) int lpf[N]; void sieve(){ for(int i=2; i<N; i++){ if(!lpf[i]){ lpf[i] = i; for(ll j=1LL*i*i; j<N; j+=i){ if(lpf[j] == 0) lpf[j] = i; } } } } int mobius[N]; int s_mobius[N]; void cmob(){ mobius[1] = 1; for(int i=2; i<N; i++){ if(lpf[i] == i) mobius[i] = -1; else{ if(lpf[i/lpf[i]] == lpf[i]) mobius[i] = 0; else mobius[i] = -1*mobius[i/lpf[i]]; } } for(int i=1; i<N; i++) s_mobius[i] = s_mobius[i-1] + mobius[i]; } void fill(int n, vector <int> &values, vector <pair <int,int> > &range){ for(int i=1; 1LL*i*i<=n; i++){ values.push_back(i); range.push_back({n/i, n/(i+1)+1}); } int last = range[range.size()-1].second; while(last > 1){ last--; values.push_back(n/last); range.push_back({last, last}); } } int main(){ sieve(); cmob(); int n; while(true){ cin >> n; if(n == 0) break; ll ans = 0; vector <int> values; vector <pair <int,int> > range; fill(n, values, range); for(int i=0; i<values.size(); i++){ int v = values[i]; ll aux = 0; vector <int> n_v; vector <pair <int,int> > n_r; fill(v, n_v,n_r); for(int j=0; j<n_v.size(); j++){ int k = n_v[j]; ll s = 1LL*k*k-(1LL*k*(k+1))/2; aux += s*(s_mobius[n_r[j].first] - s_mobius[n_r[j].second-1]); } ll s = (1LL*range[i].first*(range[i].first+1))/2; s -= (1LL*range[i].second*(range[i].second-1))/2; ans += aux*s; } cout << ans << '\n'; } return 0; }
Второе решение
Определите массив динамического программирования с помощью этой концепции:
Временная сложность: O(n^(4/3) + Q)
#include <bits/stdc++.h> using namespace std; #define N 1000005 using ll = long long; #define FIFO ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0) int lpf[N]; void sieve(){ for(int i=2; i<N; i++){ if(!lpf[i]){ lpf[i] = i; for(ll j=1LL*i*i; j<N; j+=i){ if(lpf[j] == 0) lpf[j] = i; } } } } int phi[N]; void cphi(){ phi[1] = 1; for(int i=2; i<N; i++){ if(!phi[i]){ phi[i] = i-1; for(ll j=2*i; j<N; j+=i){ if(phi[j] == 0) phi[j] = j; phi[j] = phi[j]/i*(i-1); } } } } void generate_divisors(int n, int index, int d, vector<pair <int,int> > &factorization, vector <int> &ans){ if(1LL*d*d > n) return; if(index == factorization.size()){ ans.push_back(d); if(d*d != n) ans.push_back(n/d); return; } for(int i = 0; i <= factorization[index].second; ++i){ generate_divisors(n, index+1, d, factorization, ans); d *= factorization[index].first; } } ll DP[N]; void solve(){ DP[1] = 0; DP[2] = 1; for(int i=2; i<N-1; i++){ DP[i+1] = DP[i] - (i+1); vector <pair <int,int> > f; int aux = i+1; while(aux > 1){ int d = lpf[aux]; f.push_back({d,0}); while(aux%d == 0){ f[f.size()-1].second++; aux /= d; } } vector <int> divisors; generate_divisors(i+1, 0, 1, f, divisors); for(int j=0; j<divisors.size(); j++){ DP[i+1] += 1LL*((i+1)/divisors[j])*phi[divisors[j]]; } } } int main(){ FIFO; sieve(); cphi(); solve(); int n; while(true){ cin >> n; if(n == 0) break; cout << DP[n] << '\n'; } return 0; }