PROBLEM LINK
Setter: dedsec_29
Tester: satyam_343
Editorialist: dedsec_29
PREREQUISITES
Number theory, Euler Totient Function
PROBLEM
Find the value of the following summation modulo 10^9+7
\displaystyle\sum_{i=1}^{N}\sum_{j=1}^{N}\sum_{k=1}^{i}\sum_{l=1}^{j}\gcd (k,i)\gcd(l,j)\gcd(i,j)
EXPLANATION
I will introduce some frequently used notations and lemmas first.
- [P] refers to the boolean expression, i.e. [P] = 1 when P is true, and 0 otherwise.
- d|n means that d can divide n (without a remainder).
- Let us define f\displaystyle(x) = \sum_{i=1}^{x}\gcd (x,i)
- Harmonic sum lemma:
- The sum \displaystyle\sum_{i=1}^{n}\lfloor{\frac{n}{i}}\rfloor is of order O(nlogn) because of the the Euler–Mascheroni constant
We can rewrite the problem as
\displaystyle\sum_{i=1}^{N}\sum_{j=1}^{N}f(i)f(j)\gcd(i,j)
Using the divisor sum property \sum_{d|n} \phi(d) = n, we can say that
\displaystyle \gcd(i,j) = \sum_{d|\gcd(i,j)} \phi(d) \displaystyle= \sum_{d=1}^{N} [d|i][d|j]\phi(d)
Let’s rewrite the summation using this simplification now.
\displaystyle\sum_{i=1}^{N}\sum_{j=1}^{N}f(i)f(j)\gcd(i,j)
= \displaystyle\sum_{i=1}^{N}\sum_{j=1}^{N}f(i)f(j)\sum_{d=1}^{N} [d|i][d|j]\phi(d)
= \displaystyle\sum_{d=1}^{N}\phi(d)\sum_{i=1}^{N}\sum_{j=1}^{N}[d|i]f(i)[d|j]f(j)
= \displaystyle\sum_{d=1}^{N}\phi(d)\sum_{i=1}^{N}[d|i]f(i)\sum_{j=1}^{N}[d|j]f(j)
= \displaystyle\sum_{d=1}^{N}\phi(d)(\sum_{i=1}^{N}[d|i]f(i))^2
= \displaystyle\sum_{d=1}^{N}\phi(d)(\sum_{d|i}f(i))^2
= \displaystyle\sum_{d=1}^{N}\phi(d)(\sum_{i=1}^{\lfloor{\frac{n}{d}}\rfloor}f(i\cdot d))^2
which can be calculated using two loops in O(nlogn) time using the harmonic sum lemma, given that we can precompute values of f(i).
Now it only remains to show how to calculate f(n) = \sum_{i=1}^{n}\gcd (n,i) values efficiently.
Let’s count the number of i, 1 \leq i \leq n such that [\gcd(n,i) = d]
[\gcd(n,i) = d]\implies \gcd(n/d,i/d)=1
The count of such i is \phi(n/k), therefore f(n) = \sum_{d|n} d \cdot\phi(n/d)
\phi(n) can be precomputed in O(nloglogn) time, and f(n) for all n can be computed by iterating over all divisors in O(nlogn) time. Hence overall time complexity is O(nlogn).
Calculation of phi(n)
for (int i = 1; i <= n; i++)
phi[i] = i;
for (int i = 2; i <= n; i++) {
if (phi[i] == i) {
for (int j = i; j <= n; j += i)
phi[j] -= phi[j]/i;
}
}
Calculation of f(n)
for (ll i = 1; i <= n; i++) {
for (int j = i; j <= n; j += i)
f[j] = ((phi[j/i] * i) % mod + f[j]) % mod;
}
This problem can also be solved using Principle of Inclusion-Exclusion (refer tester’s solution)
Setter's solution
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize "trapv"
#define ll long long
ll const mod = 1e9+7;
int const siz = 1e6;
ll f[siz+1], phi[siz+1];
void precomputation(int n) {
for (int i = 1; i <= n; i++)
f[i] = phi[i] = 0;
phi[0] = 1;
for (int i = 1; i <= siz; i++)
phi[i] = i;
for (int i = 2; i <= siz; i++) {
if (phi[i] == i) {
for (int j = i; j <= siz; j += i)
phi[j] -= phi[j]/i;
}
}
for (ll i = 1; i <= siz; i++) {
for (int j = i; j <= siz; j += i)
f[j] = ((phi[j/i] * i) % mod + f[j]) % mod;
}
}
void solve() {
int n; cin >> n;
precomputation(n);
ll ans = 0;
for (int i = 1; i <= n; i++) {
ll sum = 0;
for (int j = i; j <= n; j += i)
sum = (sum + f[j]) % mod;
sum = sum * sum % mod;
ans = (ans + (sum * phi[i]) % mod ) % mod;
}
cout << ans << "\n";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
solve();
return 0;
}
Tester's solution
#pragma GCC optimize("O3")
#pragma GCC target("popcnt")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimize("Ofast,unroll-loops")
#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;
#define ll long long
const ll INF_MUL=1e13;
const ll INF_ADD=1e18;
#define pb push_back
#define mp make_pair
#define nline "\n"
#define f first
#define s second
#define pll pair<ll,ll>
#define all(x) x.begin(),x.end()
#define vl vector<ll>
#define vvl vector<vector<ll>>
#define vvvl vector<vector<vector<ll>>>
#ifndef ONLINE_JUDGE
#define debug(x) cerr<<#x<<" "; _print(x); cerr<<nline;
#else
#define debug(x);
#endif
void _print(ll x){cerr<<x;}
void _print(char x){cerr<<x;}
void _print(string x){cerr<<x;}
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<class T,class V> void _print(pair<T,V> p) {cerr<<"{"; _print(p.first);cerr<<","; _print(p.second);cerr<<"}";}
template<class T>void _print(vector<T> v) {cerr<<" [ "; for (T i:v){_print(i);cerr<<" ";}cerr<<"]";}
template<class T>void _print(set<T> v) {cerr<<" [ "; for (T i:v){_print(i); cerr<<" ";}cerr<<"]";}
template<class T>void _print(multiset<T> v) {cerr<< " [ "; for (T i:v){_print(i);cerr<<" ";}cerr<<"]";}
template<class T,class V>void _print(map<T, V> v) {cerr<<" [ "; for(auto i:v) {_print(i);cerr<<" ";} cerr<<"]";}
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
typedef tree<ll, null_type, less_equal<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_multiset;
typedef tree<pair<ll,ll>, null_type, less<pair<ll,ll>>, rb_tree_tag, tree_order_statistics_node_update> ordered_pset;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
const ll MOD=1e9+7;
const ll MAX=1000000;
vector<int> phi(MAX+5);
void phi_1_to_n(int n) {
for (int i = 0; i <= n; i++)
phi[i] = i;
for (int i = 2; i <= n; i++) {
if (phi[i] == i) {
for (int j = i; j <= n; j += i)
phi[j] -= phi[j] / i;
}
}
}
void solve(){
ll n; cin>>n;
vector<ll> use(n+5,0);
phi_1_to_n(MAX);
for(ll i=1;i<=n;i++){
for(ll j=i;j<=n;j+=i){
use[j]+=i*phi[j/i];
}
}
ll ans=0;
vector<ll> anot(n+5,0);
for(ll i=n;i>=1;i--){
ll opt=0;
for(ll j=i;j<=n;j+=i){
opt+=use[j];
}
opt%=MOD;
ll now=opt*opt; now%=MOD;
for(ll j=i;j<=n;j+=i){
now-=anot[j]; now%=MOD;
}
now=(now+MOD)%MOD;
anot[i]=now;
ans+=now*i; ans%=MOD;
}
cout<<ans;
return;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
freopen("error.txt", "w", stderr);
#endif
ll test_cases=1;
//cin>>test_cases;
while(test_cases--){
solve();
}
cout<<fixed<<setprecision(10);
cerr<<"Time:"<<1000*((double)clock())/(double)CLOCKS_PER_SEC<<"ms\n";
}