#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define repr(i, n) for (int i = n-1; i >= 0; i--)
#define ALL(x) x.begin(), x.end()
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvl = vector<vll>;
const int mod = 1e9+7;
const int INF = 2e9;
const int MAX = 1e6;

struct unionfind {
    vector<int> parent; // 各要素の親のindex(根の場合は-1)
    vector<int> diff_weight; // 各要素の親要素との重さの差分(根の場合は0)
    unionfind(int n) {
        parent.resize(n, -1);
        diff_weight.resize(n, 0);
    }
    int root(int x) { // 要素xの属する集合の根のindex(要素xが根の場合はx)
        if (parent[x] < 0) return x;
        int r = root(parent[x]);
        diff_weight[x] += diff_weight[parent[x]];
        return parent[x] = r;
    }
    int weight(int x) { // 要素xとその親要素との重さの差分
        root(x);
        return diff_weight[x];
    }
    void merge(int x, int y) { // 要素xとyを共通の集合に結合
        x = root(x), y = root(y);
        if (x == y) return;
        if (parent[x] > parent[y]) {
            swap(x, y);
        }
        parent[x] += parent[y];
        parent[y] = x;
    }
    void merge(int x, int y, int w) { // 要素xとyを共通の集合に結合
        w += weight(x), w -= weight(y);
        x = root(x), y = root(y);
        if (x == y) return;
        if (parent[x] > parent[y]) {
            swap(x, y);
            w *= -1;
        }
        parent[x] += parent[y];
        parent[y] = x;
        diff_weight[y] = w;
    }
    bool common(int x, int y) { // 要素xとyが共通の集合に属するか
        return root(x) == root(y);
    }
    int size(int x) { // 要素xの属する集合の大きさ
        return -parent[root(x)];
    }
    int diff(int x, int y) { // 要素xとyの重さの差分
        return weight(y) - weight(x);
    }
};
struct edge {
    int from, to, cost;
};
struct graph {
    int n;
    vector<vector<edge>> G;
    vector<bool> visited;
    vector<int> indeg;
    graph(int _n) {
        n = _n;
        G.resize(n);
        visited.resize(n, false);
        indeg.resize(n, 0);
    }
    void add_edge(int from, int to, int cost) {
        G[from].push_back({from, to, cost});
        indeg[to]++;
    }
    void dfs(int s) {
        visited[s] = true;
        for (edge e : G[s]) {
            if (not visited[e.to]) continue;
            dfs(e.to);
        }
    }
    void dfs(int s, int i, int _n) { // 深さ制限探索
        visited[s] = true;
        if (i == _n) return;
        for (edge e : G[s]) {
            if (visited[e.to]) continue;
            dfs(e.to, i + 1, _n);
        }
    }
    void dfs(int cur, int prev, bool &closed) { // 閉路の検出
        visited[cur] = true;
        for (edge e : G[cur]) {
            if (not visited[e.to]) {
                dfs(e.to, cur, closed);
            } else if (e.to != prev) {
                closed = true;
            }
        }
    }
    void bfs(int s) {
        queue<int> q;
        q.push(s);
        visited[s] = true;
        while (not q.empty()) {
            int cur = q.front();
            for (edge e : G[cur]) {
                if (not visited[e.to]) continue;
                q.push(e.to);
                visited[e.to] = true;
            }
        }
    }
    void dijkstra(int s, vector<int> &dist) {
        using pii = pair<int, int>;
        dist.resize(n, INF);
        dist[s] = 0;
        priority_queue<pii, vector<pii>, greater<pii>> pq;
        pq.push({dist[s], s});
        while (not pq.empty()) {
            int cur = pq.top().second;
            pq.pop();
            for (edge e : G[cur]) {
                if (dist[e.to] > dist[cur] + e.cost) {
                    dist[e.to] = dist[cur] + e.cost;
                    pq.push({dist[e.to], e.to});
                }
            }
        }
    }
    void bellman_ford(int s, vector<int> &dist, bool &negative) {
        fill(visited.begin(), visited.end(), false);
        dfs(s);
        dist.resize(n, INF);
        dist[s] = 0;
        for (int i = 0; i < n; i++) {
            for (int u = 0; u < n; u++) {
                for (edge e : G[u]) {
                    if (not visited[e.to]) continue;
                    if (dist[e.to] > dist[u] + e.cost) {
                        dist[e.to] = dist[u] + e.cost;
                        if (i == n-1) {
                            negative = true;
                            return;
                        }
                    }
                }
            }
        }
    }
    void warshall_floyd(vector<vector<int>> &dist, bool &negative) {
        dist.resize(n, vector<int>(n, INF));
        for (int i = 0; i < n; i++) {
            for (edge e : G[i]) {
                dist[i][e.to] = e.cost;
            }
            dist[i][i] = 0;
        }
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][k] == INF or dist[k][j] == INF) continue;
                    if (dist[i][j] > dist[i][k] + dist[k][j]) {
                        dist[i][j] = dist[i][k] + dist[k][j];
                    }
                    if (i == j and dist[i][j] < 0) {
                        negative = true;
                        return;
                    }
                }
            }
        }
    }
    static bool comp(const edge &e1, const edge &e2) {
        return e1.cost < e2.cost;
    }
    void kruskal(vector<edge> &edges, int &sum) {
        unionfind mst(n);
        sort(edges.begin(), edges.end(), comp);
        int m = edges.size();
        sum = 0;
        for (int i = 0; i < m; i++) {
            edge e = edges[i];
            if (not mst.common(e.from, e.to)) {
                mst.merge(e.from, e.to);
                sum += e.cost;
            }
        }
    }
    void topological(vector<int> &t) {
        queue<int> q;
        for(int i = 0; i < n; i++) {
            if (indeg[i] == 0) {
                q.push(i);
            }
        }
        while (not q.empty()) {
            int cur = q.front();
            q.pop();
            t.push_back(cur);
            for (edge e : G[cur]) {
                indeg[e.to]--;
                if (indeg[e.to] == 0) {
                    q.push(e.to);
                }
            }
        }
    }
};
template<typename T> struct cusum {
    int n;
    vector<T> s;
    cusum(vector<T> &a) {
        n = a.size();
        s.resize(n + 1);
        s[0] = 0;
        for (int i = 1; i <= n; i++) {
            s[i] = s[i - 1] + a[i - 1];
        }
    }
    T sum(int a, int b) { // 半開区間[a,b)の和(0-indexed)
        return s[b] - s[a];
    }
};
template<typename T> struct BIT {
    int MAX;
    vector<T> bit;
    BIT(int n) {
        MAX = n;
        bit.resize(MAX + 1, 0);
    }
    void add(int i, int x) { // i番目(1-indexed)に値xを追加
        while (i <= MAX) {
            bit[i] += x;
            i += i & -i;
        }
    }
    T sum(int i) { // 閉区間[1,i]の和
        T s = 0;
        while (i > 0) {
            s += bit[i];
            i -= i & -i;
        }
        return s;
    }
    T rsq(int a, int b) { // 閉区間[a,b]の和
        return sum(b) - sum(a - 1);
    }
};
template<typename T> bool is_prime(T x) { // 素数判定 
    if (x < 2) return false;
    for (T i = 2; i * i <= x; i++) {
        if (x % i == 0) return false;
    }
    return true;
}
template<typename T> void prime_fact(T x, vector<T> &factor) { // 素因数分解
    for (T i = 2; i * i <= x; i++) {
        while (x % i == 0) {
            factor.push_back(i);
            x /= i;
        }
    }
    if (x != 1) {
        factor.push_back(x);
    }
}
struct erat {
    int MAX;
    vector<int> seive;
    erat(int n) { // エラトステネスのふるい
        MAX = n;
        seive.resize(MAX+1);
        for (int i = 0; i <= MAX; i++) {
            seive[i] = i;
        }
        for (int i = 2; i <= MAX; i++) {
            if (seive[i] == i) { // seive[i]はiの最大の素因数
                for (int j = i + i; j <= MAX; j += i) {
                    seive[j] = i;
                }
            }
        }
    }
    void fact(int x, vector<int> &factor) { // 高速素因数分解
        if (x < 2) return;
        while (x > 1 and x % seive[x] == 0) {
            factor.push_back(seive[x]);
            x /= seive[x];
        }
        reverse(factor.begin(), factor.end());
    }
    bool is_prime(int x) { // 素数判定
        if (x >= 2 and seive[x] == x) return true;
        return false;
    }
};
template<typename T> T gcd(T a, T b) { // 整数aとbの最大公約数（ユークリッドの互除法）
    if (b == 0) return a;
    return gcd(b, a % b);
}
template<typename T> T gcd(vector<T> &a) { //  3つ以上の整数の最大公約数
    int n = (int)a.size();
    T r;
    for (int i = 0; i < n; i++) {
        if (i == 0) r = a[i];
        r = gcd(r, a[i]);
    }
    return r;
}
template<typename T> T lcm(T a, T b) { // 整数aとbの最小公倍数
    return a / gcd(a, b) * b;
}
template<typename T> T lcm(vector<T> &a) { // 3つ以上の整数の最小公倍数
    int n = (int)a.size();
    T r = 1;
    for (int i = 0; i < n; i++) {
        r = lcm(r, a[i]);
    }
    return r;
}
template<typename T> T digit_sum(T n) { // 整数nの格桁の和
    T sum = 0;
    while (n > 0) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}
template<typename T> T digit_sum(string s) { // 整数nの格桁の和
    int n = (int)s.size();
    T sum = 0;
    for (int i = 0; i < n; i++) {
        sum += s[i] - '0';
    }
    return sum;
}
int eular(int n) { // nと互いに素であるn以下の自然数の個数
    int p = n;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            p -= p / i;
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) p -= p / n;
    return p;
}
template<typename T> bool is_triangle(T a, T b, T c) { // 三角形の成立条件
    if (a + b > c && b + c > a && c + a > b) return true;
    else return false;
}
template<typename T> struct combination {
    int MAX;
    vector<T> f;
    vector<vector<T>> dp;
    combination(int _n) {
        MAX = _n;
        f.resize(MAX + 1, 0);
        for (int i = 0; i <= MAX; i++) {
            if (i == 0) f[i] = 1;
            else f[i] = i * f[i-1] % mod;
        }
        dp.resize(MAX + 1, vector<T>(MAX + 1, 0));
    }
    T power(int m, int n) { // m^n
        if (n == 0) return 1;
        if (n % 2 == 1) return m * power(m, n-1) % mod;
        T r = power(m, n/2);
        return r * r % mod;
    }
    T inv(int a, int b) { // a / b % mod
        return a * power(b, mod-2) % mod;
    }
    T fact(int n) { // n!
        return f[n];
    }
    T perm(int n, int r) { // nPr
        if (n < r) return 0;
        return inv(fact(n), fact(n-r));
    }
    T comb(int n, int r) { // nCr
        if (n < r) return 0;
        return inv(perm(n, r), fact(r));
    }
    T pie(int n, int k) { // 包除原理
        T s = 0;
        for (int i = 0; i < k; i++) {
            s += power(-1, i) * comb(k, i) * power(k-i, n);
            s %= mod;
            if (s < 0) s += mod;
        }
        return s;
    }
    T part(int n, int k) { // 分割数
        if (n < 0) return 0;
        if (n == 0 || k == 1) return dp[n][k] = 1;
        if (dp[n][k] != 0) return dp[n][k];
        return dp[n][k] = (part(n, k-1) + part(n-k, k)) % mod;
    }
    T stir(int n, int k) { // スターリング数
        if (n < k || (n > 0 && k == 0)) return 0;
        if (n == k || k == 1) return dp[n][k] = 1;
        if (dp[n][k] != 0) return dp[n][k];
        return dp[n][k] = (stir(n-1, k-1) + k * stir(n-1, k)) % mod;
    }
    T bell(int n, int k) { // ベル数
        T s = 0;
        for (int i = 0; i <= k; i++) {
            s += stir(n, i);
            s %= mod;
        }
        return s;
    }
};

int main() {
    int n;
    cin >> n;
    erat p(n);
    int a, b ,c;
    int ans = 0;
    for (int c = 1; c < n; c++) {
        int ab = n - c;
        int r = 1;
        for (int i = 2; i * i <= ab; i++) {
            if (ab % i == 0) {
                int cnt = 1;
                while (ab % i == 0) {
                    ab /= i;
                    cnt++;
                }
                r *= cnt;
            }
        }
        if (ab != 1) r *= 2;
        ans += r;
    }
    cout << ans << endl;
}

Measured memory_bytes: 7132