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

#define si(a) scanf("%d",&a)
#define MAX 200010
#define MAX_lg 20
#define minchar 'a'
#define Index(a) (((a) < len) ? index[mlgn][a] : -1)
int Count[MAX], output[MAX];
int pos[MAX], index[MAX_lg][MAX], lcp[MAX], len, mlgn;
struct suffixArray
{

    suffixArray() {}
    suffixArray(const char *S)
    {
        create(S);
    }
    void CountSort(int add)
    {
        int to = max(len, 26);
        memset(Count, 0, sizeof(Count));
        for(int i = 0; i < len; i++) ++Count[ Index(pos[i] + add) + 1 ];
        for(int i = 1; i <= to; i++) Count[i] += Count[i-1];
        for(int i = len-1; i >= 0; i--) output[ --Count[ Index(pos[i] + add) + 1 ] ] = pos[i];
        for(int i = 0; i < len; i++) pos[i] = output[i];
    }
    int Lcp(int x, int y)
    {
        int ret = 0;
        for(int i = mlgn; i >= 0 && x < len && y < len; i--) if(index[i][x] == index[i][y])
                x += (1 << i),
                     y += (1 << i),
                          ret += (1 << i);
        return ret;
    }
    void create(const char *S)
    {
        len = strlen(S), mlgn = 0;
        for(int i = 0; i < len; i++) index[0][i] = S[i] - minchar;
        for(int done = 1; done < len; done <<= 1)
        {
            for(int i = 0; i < len; i++) pos[i] = i;
            CountSort(done);
            CountSort(0);
            for(int i = 0; i < len; i++)
            {
                int sc1 = (i > 0) ? Index(pos[i-1] + done) : -1;
                int sc2 = Index(pos[i] + done);
                index[mlgn+1][pos[i]] = (i > 0 && index[mlgn][pos[i]] == index[mlgn][pos[i-1]] && sc1 == sc2) ? index[mlgn+1][pos[i-1]] : i;
            }
            mlgn++;
        }
        lcp[0] = 0;
        for(int i = 1; i < len; i++) lcp[i] = Lcp(pos[i], pos[i-1]);
    }
};

char str[MAX];

int main()
{
    //freopen("input.txt","r",stdin);
    scanf("%s",str);
    suffixArray ob(str);
    long long ans=len-pos[0],ttt=ans;
    stack<int> st;
    st.push(ttt);
    for(int i=1;i<len;i++){
        int cnt=0;
        while(!st.empty()){
            int now=st.top();
            if(now<=lcp[i])break;
            cnt++;
            st.pop();
            ttt-=now;
        }
        while(cnt){
            st.push(lcp[i]);
            ttt+=lcp[i];
            cnt--;
        }
        ttt+=(len-pos[i]);
        st.push(len-pos[i]);
        ans+=ttt;
    }
    cout<<ans<<endl;
    return 0;
}


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

#define si(a) scanf("%d",&a)
#define MAX 100010
#define MAX_lg 20
#define minchar 'a'
#define Index(a) (((a) < len) ? index[mlgn][a] : -1)
int Count[MAX], output[MAX];
int pos[MAX], index[MAX_lg][MAX], lcp[MAX], len, mlgn;
struct suffixArray
{

    suffixArray() {}
    suffixArray(const char *S)
    {
        create(S);
    }
    void CountSort(int add)
    {
        int to = max(len, 26);
        memset(Count, 0, sizeof(Count));
        for(int i = 0; i < len; i++) ++Count[ Index(pos[i] + add) + 1 ];
        for(int i = 1; i <= to; i++) Count[i] += Count[i-1];
        for(int i = len-1; i >= 0; i--) output[ --Count[ Index(pos[i] + add) + 1 ] ] = pos[i];
        for(int i = 0; i < len; i++) pos[i] = output[i];
    }
    int Lcp(int x, int y)
    {
        int ret = 0;
        for(int i = mlgn; i >= 0 && x < len && y < len; i--) if(index[i][x] == index[i][y])
                x += (1 << i),
                     y += (1 << i),
                          ret += (1 << i);
        return ret;
    }
    void create(const char *S)
    {
        len = strlen(S), mlgn = 0;
        for(int i = 0; i < len; i++) index[0][i] = S[i] - minchar;
        for(int done = 1; done < len; done <<= 1)
        {
            for(int i = 0; i < len; i++) pos[i] = i;
            CountSort(done);
            CountSort(0);
            for(int i = 0; i < len; i++)
            {
                int sc1 = (i > 0) ? Index(pos[i-1] + done) : -1;
                int sc2 = Index(pos[i] + done);
                index[mlgn+1][pos[i]] = (i > 0 && index[mlgn][pos[i]] == index[mlgn][pos[i-1]] && sc1 == sc2) ? index[mlgn+1][pos[i-1]] : i;
            }
            mlgn++;
        }
        lcp[0] = 0;
        for(int i = 1; i < len; i++) lcp[i] = Lcp(pos[i], pos[i-1]);
    }
};

char str[MAX];

int main()
{
    //freopen("input.txt","r",stdin);
    scanf("%s",str);
    suffixArray ob(str);
    long long ans=len-pos[0],ttt=ans;
    stack<int> st;
    st.push(ttt);
    for(int i=1;i<len;i++){
        int cnt=0;
        while(!st.empty()){
            int now=st.top();
            if(now<=lcp[i])break;
            cnt++;
            st.pop();
            ttt-=now;
        }
        while(cnt){
            st.push(lcp[i]);
            ttt+=lcp[i];
            cnt--;
        }
        ttt+=(len-pos[i]);
        st.push(len-pos[i]);
        ans+=ttt;
    }
    cout<<ans<<endl;
    return 0;
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> P;

const int MN = 100010;
int n, k;
int sa[MN], rsa[MN], tmp[MN], lcp[MN];
string s;
int p[MN], sz[MN];
P con[MN];

int group(int x) {
    if (x == p[x]) return x;
    return p[x] = group(p[x]);
}

void unite(int x, int y) {
    x = group(x);
    y = group(y);
    assert(x != y);
    if (sz[x] > sz[y]) swap(x, y);
    sz[y] += sz[x];
    p[x] = y;
}

bool cmp(int x, int y) {
    if (rsa[x] != rsa[y]) return rsa[x] < rsa[y];
    int rx = x + k <= n ? rsa[x + k] : -1;
    int ry = y + k <= n ? rsa[y + k] : -1;
    return rx < ry;
}

void SA() {
    for (int i = 0; i <= n; i++) {
        sa[i] = i;
        rsa[i] = i < n ? s[i] : -1;
    }
    for (k = 1; k <= n; k *= 2) {
        sort(sa, sa + n + 1, cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i <= n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + int(cmp(sa[i - 1], sa[i]));
        }
        copy(tmp, tmp + n + 1, rsa);
    }

    for (int i = 0, h = 0; i < n; i++) {
        int j = sa[rsa[i] - 1];
        h = max(h - 1, 0);
        while (i + h < n && j + h < n && s[i + h] == s[j + h]) {
            h++;
        }
        lcp[rsa[i] - 1] = h;
    }
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);

    cin >> s;
    n = int(s.size());
    SA();

    for (int i = 0; i <= n; i++) {
        p[i] = i;
        sz[i] = 1;
    }
    for (int i = 0; i < n; i++) {
        con[i] = P(lcp[i], i);
    }
    sort(con, con + n);
    ll ans = 0, acc = 0;
    for (int i = n, j = n - 1; i >= 1; i--) {
        while (j >= 0 && con[j].first == i) {
            int x = con[j].second;
            acc += ll(sz[group(x)]) * sz[group(x + 1)];
            unite(x, x + 1);
            j--;
        }
        ans += acc;
    }
    ans += ll(n) * (n + 1) / 2;
    cout << ans << endl;
    return 0;
}


#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int inf = numeric_limits<int>::max();

namespace SA {
const int MN = 100010;
int rsa[MN], tmp[MN];
int n, k;

bool cmp(int x, int y) {
    if (rsa[x] != rsa[y]) return rsa[x] < rsa[y];
    int rx = (x+k<=n) ? rsa[x+k] : -1;
    int ry = (y+k<=n) ? rsa[y+k] : -1;
    return rx < ry;
}

template <class S> vector<int> exec(const S &s) {
    n = int(s.size());
    vector<int> sa(n+1), lcp(n);

    for (int i = 0; i <= n; i++) {
        sa[i] = i;
        rsa[i] = i < n ? s[i] : -1;
    }
    for (k = 1; k <= n; k *= 2) {
        sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i <= n; i++) {
            tmp[sa[i]] = tmp[sa[i-1]] + int(cmp(sa[i-1], sa[i]));
        }
        copy(tmp, tmp+n+1, rsa);
    }

    for (int i = 0; i <= n; i++) rsa[sa[i]] = i;
    int h = 0;
    for (int i = 0; i < n; i++) {
        int j = sa[rsa[i]-1];
        if (h) h--;
        while (i+h < n && j+h<n && s[i+h] == s[j+h]) {
            h++;
        }
        lcp[rsa[i]-1] = h;
    }

    return lcp;
}
}

const int MN = 100010;
int le[MN], ri[MN];

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);

    string s;
    cin >> s;
    vector<int> lcp = SA::exec(s);
    int n = int(s.size());

    vector<int> st;

    for (int i = 1; i < n; i++) {
        while (st.size() && lcp[st.back()] > lcp[i]) st.pop_back();
        le[i] = (st.size()) ? st.back()+1 : 1;
        st.push_back(i);
    }

    st.clear();
    for (int i = n-1; i >= 1; i--) {
        while (st.size() && lcp[st.back()] >= lcp[i]) st.pop_back();
        ri[i] = (st.size()) ? st.back()-1 : n-1;
        st.push_back(i);
    }

    ll ans = ll(n)*(n+1)/2;
    for (int i = 1; i < n; i++) {
        ll w = ll(i-le[i])*(ri[i]-i)+(i-le[i])+(ri[i]-i)+1;
        ans += w*lcp[i];
    }
    cout << ans << '\n';
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define maxn 200100
#define ll long long
struct State
{
	State *pre,*go[26];
	int len,minlen,cnt;
	void clear()
	{
		pre=0;
		len=0;
		cnt=1;
		minlen=0;
		memset(go,0,sizeof(go));
	}
}*root,*last;
State sam[maxn<<1],*cur,*b[maxn<<1];
int cnt[maxn];
void init()
{
	cur=sam;
	root=last=cur++;
	root->clear();
}
void extend(int w)
{
	State *p=last;
	State *np=cur++;
	np->clear();
	np->len=p->len+1;
	while(p&&!p->go[w])
		p->go[w]=np,p=p->pre;
	if(p==0)
		np->pre=root;
	else
	{
		State *q=p->go[w];
		if(p->len+1==q->len)
			np->pre=q;
		else
		{
			State *nq=cur++;
			nq->clear();
			memcpy(nq->go,q->go,sizeof(q->go));
			nq->len=p->len+1;
			nq->pre=q->pre;
			q->pre=nq;
			np->pre=nq;
			while(p&&p->go[w]==q)
				p->go[w]=nq,p=p->pre;
		}
	}
	last=np;
}
char s[maxn];
int main()
{
	scanf("%s",s);
	init();
	int len=strlen(s);
	for(int i=0;i<len;++i) extend(s[i]-'a');
	memset(cnt,0,sizeof(cnt));
	for(State *p=sam;p!=cur;++p)
		cnt[p->len]++;
	for(int i=1;i<=len;++i) cnt[i]+=cnt[i-1];
	int tot=cnt[len];
	for(State *p=sam;p!=cur;++p)
		b[--cnt[p->len]]=p;
	ll ans=0;
	for(int i=tot-1;i>=1;--i)
	{
		b[i]->minlen=b[i]->pre->len+1;
		b[i]->pre->cnt+=b[i]->cnt;
		//printf("cnt1=%d,cnt2=%d\n",b[i]->cnt,b[i]->pre->cnt);
		//printf("len=%d,minlen=%d,cnt=%d\n",b[i]->len,b[i]->minlen,b[i]->cnt);
		ans+=1LL*(b[i]->len-b[i]->minlen+1)*b[i]->cnt*(b[i]->cnt+1)/2;
	}
	printf("%lld\n",ans);
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define maxn 200100
#define ll long long
struct State
{
	State *pre,*go[26];
	int len,minlen,cnt;
	void clear()
	{
		pre=0;
		len=0;
		cnt=0;
		minlen=0;
		memset(go,0,sizeof(go));
	}
}*root,*last;
State sam[maxn<<1],*cur,*b[maxn<<1];
int cnt[maxn];
void init()
{
	cur=sam;
	root=last=cur++;
	root->clear();
}
void extend(int w)
{
	State *p=last;
	State *np=cur++;
	np->clear();
	np->len=p->len+1;
	while(p&&!p->go[w])
		p->go[w]=np,p=p->pre;
	if(p==0)
		np->pre=root;
	else
	{
		State *q=p->go[w];
		if(p->len+1==q->len)
			np->pre=q;
		else
		{
			State *nq=cur++;
			nq->clear();
			memcpy(nq->go,q->go,sizeof(q->go));
			nq->len=p->len+1;
			nq->pre=q->pre;
			q->pre=nq;
			np->pre=nq;
			while(p&&p->go[w]==q)
				p->go[w]=nq,p=p->pre;
		}
	}
	last=np;
}
char s[maxn];
int main()
{
	scanf("%s",s);
	init();
	int len=strlen(s);
	for(int i=0;i<len;++i) extend(s[i]-'a');
	memset(cnt,0,sizeof(cnt));
	for(State *p=sam;p!=cur;++p)
		cnt[p->len]++;
	for(int i=1;i<=len;++i) cnt[i]+=cnt[i-1];
	int tot=cnt[len];
	for(State *p=sam;p!=cur;++p)
		b[--cnt[p->len]]=p;
	ll ans=0;
	State *p=root;
	for(int i=0;i<len;++i)
	{
		p=p->go[s[i]-'a'];
		p->cnt=1;
	}
	for(int i=tot-1;i>=1;--i)
	{
		//b[i]->cnt=max(b[i]->cnt,1);
		b[i]->minlen=b[i]->pre->len+1;
		b[i]->pre->cnt+=b[i]->cnt;
		//printf("cnt1=%d,cnt2=%d\n",b[i]->cnt,b[i]->pre->cnt);
		//printf("len=%d,minlen=%d,cnt=%d\n",b[i]->len,b[i]->minlen,b[i]->cnt);
		ans+=1LL*(b[i]->len-b[i]->minlen+1)*b[i]->cnt*(b[i]->cnt+1)/2;
	}
	printf("%lld\n",ans);
	return 0;
}


// template.cpp : Defines the entry point for the console application.
//
#include <climits>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <list>
#include <tuple>
#include <ctime>
#include <cassert>
using namespace std;

//type shortcuts
typedef long long ll;
typedef vector<ll> VI;
typedef long double DOUBLE;
typedef vector<DOUBLE> VD;
typedef vector<VD> VVD;

//constants
const DOUBLE EPS=1e-9;
const DOUBLE PI = atan(1) * 4;
const ll M = 1000000007;

//Geometry (real number coordinates)
struct PT {
    double x, y;
    PT() {}
    PT(double x, double y) : x(x), y(y) {}
    PT(const PT &p) : x(p.x), y(p.y) {}
    PT operator + (const PT &p)  const { return PT(x + p.x, y + p.y); }
    PT operator - (const PT &p)  const { return PT(x - p.x, y - p.y); }
    PT operator * (double c)     const { return PT(x*c, y*c); }
    PT operator / (double c)     const { return PT(x / c, y / c); }
};
double dot(PT p, PT q) { return p.x*q.x + p.y*q.y; }
double dist2(PT p, PT q) { return dot(p - q, p - q); }
double cross(PT p, PT q) { return p.x*q.y - p.y*q.x; }
double cos_u2v(const PT& u, const PT& v) { return dot(u, v) / sqrt(dot(u,u)*dot(v,v)); }
double sin_u2v(const PT& u, const PT& v) { return cross(u, v) / sqrt(dot(u, u)*dot(v, v)); }
ostream &operator<<(ostream &os, const PT &p) {
    os << "(" << p.x << "," << p.y << ")";
}
double vector2angle(const PT &v) {return atan2(v.y, v.x);}

//union find
struct UnionFind {
    vector<ll> C;
    UnionFind(ll n) : C(n) {
        for (int i=0;i<n;i++) C[i]=i;
    }
    ll find(ll x) {
        return (C[x] == x) ? x : C[x] = find(C[x]);
    }
    void merge(ll x, ll y) {
        C[find(x)] = find(y);
    }
};

//euclid, modulo
ll mod(ll x, ll m) { return ((x%m) + m) % m; }
ll gcd(ll a, ll b) { for (;b>0;swap(a, b))a %= b;return a; }
ll extended_euclid(ll a, ll b, ll &x, ll &y) {
    ll xx = y = 0;
    ll yy = x = 1;
    while (b) {
        ll q = a / b;
        ll t = b; b = a%b; a = t;
        t = xx; xx = x - q*xx; x = t;
        t = yy; yy = y - q*yy; y = t;
    }
    return a;
}
ll mod_inverse(ll a, ll n) {
    ll x, y;
    ll d = extended_euclid(a, n, x, y);
    if (d > 1) return -1;
    return mod(x, n);
}

//Integer discretization. Return number of values.
ll discretize(vector<ll> &x){
    set<ll> avset(x.begin(),x.end());
    map<ll,ll> vmap;ll dn=0;
    for (auto it=avset.begin();it!=avset.end();it++) vmap[*it]=dn++;
    for (ll i=0,n=x.size();i<n;i++) x[i]=vmap[x[i]];
    return dn;
}

/**
 * Two-phase simplex.
 *
 * \param   A -- m x n matrix
 * \param   b -- m-dim vector
 * \param   c -- n-dim vector
 * \return  x -- an n-dim vector for receiving solution.
 * \return  value (infinity if unbounded above, nan if infeasible)
 *
 * \assume  The problem is:
 *    maximize     c^T x
 *    subject to   Ax <= b
 *                 x >= 0
 *
 * \usage
 *      LPSolver lp=LPSolver(A,b,c);
 *      DOUBLE value=lp.solve(x);
 */
struct LPSolver {
    int m, n;
    VI B, N;
    VVD D;
    LPSolver(const VVD &A, const VD &b, const VD &c) :
        m(b.size()), n(c.size()), N(n + 1), B(m), D(m + 2, VD(n + 2)) {
        for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) D[i][j] = A[i][j];
        for (int i = 0; i < m; i++) { B[i] = n + i; D[i][n] = -1; D[i][n + 1] = b[i]; }
        for (int j = 0; j < n; j++) { N[j] = j; D[m][j] = -c[j]; }
        N[n] = -1; D[m + 1][n] = 1;
    }
    void Pivot(int r, int s) {
        for (int i = 0; i < m + 2; i++) if (i != r)
            for (int j = 0; j < n + 2; j++) if (j != s)
                D[i][j] -= D[r][j] * D[i][s] / D[r][s];
        for (int j = 0; j < n + 2; j++) if (j != s) D[r][j] /= D[r][s];
        for (int i = 0; i < m + 2; i++) if (i != r) D[i][s] /= -D[r][s];
        D[r][s] = 1.0 / D[r][s];
        swap(B[r], N[s]);
    }
    bool Simplex(int phase) {
        int x = phase == 1 ? m + 1 : m;
        while (true) {
            int s = -1;
            for (int j = 0; j <= n; j++) {
                if (phase == 2 && N[j] == -1) continue;
                if (s == -1 || D[x][j] < D[x][s] || D[x][j] == D[x][s] && N[j] < N[s]) s = j;
            }
            if (D[x][s] > -EPS) return true;
            int r = -1;
            for (int i = 0; i < m; i++) {
                if (D[i][s] < EPS) continue;
                if (r == -1 || D[i][n + 1] / D[i][s] < D[r][n + 1] / D[r][s] ||
                    (D[i][n + 1] / D[i][s]) == (D[r][n + 1] / D[r][s]) && B[i] < B[r]) r = i;
            }
            if (r == -1) return false;
            Pivot(r, s);
        }
    }
    DOUBLE solve(VD &x) {
        int r = 0;
        for (int i = 1; i < m; i++) if (D[i][n + 1] < D[r][n + 1]) r = i;
        if (D[r][n + 1] < -EPS) {
            Pivot(r, n);
            if (!Simplex(1) || D[m + 1][n + 1] < -EPS) return -numeric_limits<DOUBLE>::infinity();
            for (int i = 0; i < m; i++) if (B[i] == -1) {
                int s = -1;
                for (int j = 0; j <= n; j++)
                    if (s == -1 || D[i][j] < D[i][s] || D[i][j] == D[i][s] && N[j] < N[s]) s = j;
                Pivot(i, s);
            }
        }
        if (!Simplex(2)) return numeric_limits<DOUBLE>::infinity();
        x = VD(n);
        for (int i = 0; i < m; i++) if (B[i] < n) x[B[i]] = D[i][n + 1];
        return D[m][n + 1];
    }
};

/**
 * Build adjacent lists from edges.
 *
 * \param n     vertex 0 .. n-1
 * \param froms edge.from
 * \param tos   edge.to
 *
 * \assume fromes.len==tos.len
 *
 * \return v2e0 first edge
 * \return e2n  edge.next
 */
void buildadjlist(ll n,
    const vector<ll> &froms, const vector<ll> &tos,
    vector<ll> &v2e0, vector<ll> &e2n)
{
    v2e0.resize(n);
    v2e0.assign(n, -1);
    ll en = froms.size();
    e2n.resize(en);
    for (int i = 0;i < en;i++) {
        ll f = froms[i];
        ll t = tos[i];
        e2n[i] = v2e0[f];
        v2e0[f] = i;
    }
}

//dijkstra
void dijkstra(const vector<ll>& v2e0,
        const vector<ll>& e2t, const vector<ll>& e2c, const vector<ll>& e2n,
        ll src, vector<ll>& dist, vector<ll>& prec)
{
    ll n=v2e0.size();
    dist.resize(n);dist.assign(n,INT_MAX);
    prec.resize(n);prec.assign(n,-1);
    vector<bool> arr(n);
    ll sel=src;
    dist[sel]=0;
    while (sel>=0){
        arr[sel]=true;
        for (ll i=v2e0[sel];i>=0;i=e2n[i]){
            ll to=e2t[i];
            ll cost=e2c[i];
            if (!arr[to]&&dist[to]>dist[sel]+cost){
                dist[to]=dist[sel]+cost;
                prec[to]=sel;
            }
        }
        sel=-1;
        for (ll i=0;i<n;i++)
            if (!arr[i]&&(sel==-1||dist[i]<dist[sel])) sel=i;
    }
}

//Tarjan's strong connected components
void tarjan_dfs(ll v, ll &timer, ll &cn,
        vector<ll> &vtime, vector<ll> &rtime,
        const vector<ll> &v2e0,
        const vector<ll> &e2t, const vector<ll> &e2n,
        vector<ll> &v2c, stack<ll> &stk)
{
    stk.push(v);
    rtime[v]=vtime[v]=timer++;
    for (ll i=v2e0[v];i>=0;i=e2n[i]){
        ll w=e2t[i];
        if (vtime[w]==-1){
            tarjan_dfs(w,timer,cn,vtime,rtime,v2e0,e2t,e2n,v2c,stk);
            rtime[v]=min(rtime[v],rtime[w]);
        }else if (v2c[w]==-1)
            rtime[v]=min(rtime[v],vtime[w]);
    }
    if (vtime[v]==rtime[v]){
        while (true){
            ll pv=stk.top();
            v2c[pv]=cn;
            stk.pop();
            if (pv==v) break;
        }
        cn++;
    }
}
void tarjan_strong(ll n,
        const vector<ll> &v2e0,
        const vector<ll> &e2t,
        const vector<ll> &e2n,
        vector<ll> &v2c)
{
    v2c.assign(n,-1);
    vector<ll> vtime(n,-1);
    vector<ll> rtime(n,-1);
    stack<ll> stk;
    ll timer=0,cn=0;
    for (ll i=0;i<n;i++){
        if (vtime[i]>=0) continue;
        tarjan_dfs(i,timer,cn,vtime,rtime,v2e0,e2t,e2n,v2c,stk);
    }
}

/**
 * Ford Fulkerson max flow algorithm.
 *
 * O(E*maxflow) time.
 * Graph is represented by adjacent lists.
 *
 * \assume source!=sink.
 *
 * \param v2e0  vertex.first_edge
 * \param e2f   edge.from
 * \param e2t   edge.to
 * \param e2c   edge.cost
 * \param e2n   edge.next
 * \param s     source
 * \param t     sink
 *
 * \return  maxflow
 *
 * \usage
 *      FordFulk ff(v2e0,e2f,e2t,e2c,e2n,s,t);
 *      ll sol=ff.eval();
 */
struct FordFulk {
    const vector<ll> &v2e0;
    const vector<ll> &e2f;
    const vector<ll> &e2t;
    const vector<ll> &e2c;
    const vector<ll> &e2n;
    ll s,t;
    FordFulk(const vector<ll> &_v2e0,
        const vector<ll> &_e2f,
        const vector<ll> &_e2t,
        const vector<ll> &_e2c,
        const vector<ll> &_e2n,
        ll _s, ll _t)
        :  s(_s),t(_t),v2e0(_v2e0),e2f(_e2f),e2t(_e2t),e2c(_e2c),e2n(_e2n)
    {}
    ll _find_aug_path(const vector<ll> &v2e0,
        const vector<ll> &e2f,
        const vector<ll> &e2t,
        const vector<ll> &e2c,
        const vector<ll> &e2n,
        ll s, ll t, vector<ll> &epath)
    {
        vector<ll> q2v;
        vector<ll> q2p;
        vector<ll> q2e;
        vector<ll> q2f;
        vector<bool> inq(v2e0.size(), false);
        //BFS
        q2v.push_back(s);
        q2p.push_back(-1);
        q2e.push_back(-1);
        q2f.push_back(INT_MAX);
        inq[s] = true;
        for (ll i = 0;i<q2v.size();i++) {
            ll crt = q2v[i];
            ll f = q2f[i];
            for (ll j = v2e0[crt];j != -1;j = e2n[j]) {
                ll to = e2t[j];
                ll rem = e2c[j];
                if (inq[to]) continue;
                if (rem <= 0) continue;
                q2v.push_back(to);
                q2p.push_back(i);
                q2e.push_back(j);
                q2f.push_back(min(f, rem));
                inq[to] = true;
            }
        }
        //Get Path
        for (ll i = 0;i<q2v.size();i++) {
            if (q2v[i] != t) continue;
            stack<ll> stk;
            for (ll j = i;j>0;j = q2p[j]) {
                stk.push(q2e[j]);
            }
            while (!stk.empty()) {
                epath.push_back(stk.top());
                stk.pop();
            }
            return q2f[i];
        }
        return 0;
    }

    ll eval(){
        //Build residual graph
        ll en = e2f.size();
        map<pair<ll, ll>, ll> rsd;
        for (ll i = 0;i<en;i++) {
            ll f = e2f[i];
            ll t = e2t[i];
            rsd[make_pair(f, t)] += e2c[i];
            rsd[make_pair(t, f)] += 0;
        }
        map<pair<ll, ll>, ll> nei;
        ll ren = 0;
        for (auto er : rsd) {
            nei[er.first] = ren++;
        }
        ll rvn = v2e0.size();
        vector<ll> rv2e0(rvn, -1);
        vector<ll> re2f(ren);//from
        vector<ll> re2t(ren);//to
        vector<ll> re2c(ren);//cost
        vector<ll> re2n(ren);//next edge
        vector<ll> re2p(ren);//peer edge
        ll rei = 0;
        for (auto er : rsd) {
            ll f = er.first.first;
            ll t = er.first.second;
            ll c = er.second;
            re2f[rei] = f;
            re2t[rei] = t;
            re2c[rei] = c;
            re2p[rei] = nei[make_pair(t, f)];
            re2n[rei] = rv2e0[f];
            rv2e0[f] = rei;
            ++rei;
        }

        //FF main
        ll sol = 0;
        vector<ll> epath;
        while (true) {
            epath.clear();
            ll res = _find_aug_path(rv2e0, re2f, re2t, re2c, re2n, s, t, epath);
            if (res == 0) break;
            for (auto ei : epath) {
                re2c[ei] -= res;
                re2c[re2p[ei]] += res;
            }
            sol += res;
        }
        return sol;
    }
};

// 1D Binary indexed tree.
// Point update(by relative diff) & [0..k] interval query.
template <typename T>
struct BIT1D {
    ll n;
    vector<T> v;
    T (*fnx)(ll,T);
    T (*fab)(T,T);
    ll rg(ll k){return ((k^(k+1))+1)/2;}
    BIT1D (ll _n, T v0, T (*_fnx)(ll,T), T (*_fab)(T,T))
            :n(_n),v(_n),fnx(_fnx),fab(_fab)
    {for (ll i=0;i<n;i++) v[i]=fnx(rg(i),v0);}
    void update(ll i, T v1){
        for (;i<n;i+=rg(i)) v[i]=fab(v[i],v1);
    }
    T query(ll i){
        ll j=i-rg(i);
        return (j<0)?v[i]:(query(j)+v[i]);
    }
};

/**
 * 1D generic segment tree. Lazy propgation & on-demand allocation.
 *
 * Allow 1 type of segmentable interval query(SUM/MAX),
 *       interval update by relative diff(each+1/each-lift-to-5), and
 *       interval update by value-setting(each-set-to-9)
 *
 * \param _l    left-most position
 * \param _r    right-most position
 * \param _v    initial element value
 * \param _fnx  query function I    (compute X+X+X+...+X)
 * \param _fab  query function II   (compute A+B)
 *
 * \usage
 *      double max_ab(double a, double b){return max(a,b);}
 *      double max_nx(int n,double x){return x;}
 *      LazySegTree1D<R> t(0,99,0.0,max_nx,max_ab);//query MAX(seg) on [0,99]
 *      tree.updres(1,6,2.2); //lift each to 2.2 on [1,6]
 *      tree.setval(0,5,1.1); //set each to 1.1 on [0,5]
 *      auto v=tree.get(2,7); // get maximum on [2,7]
 */
template <typename T>
struct LazySegTree1D {
    vector<T> v;
    vector<ll> opr;//0: set value; 1: update result; -1: idle
    vector<T> opd;
    vector<ll> nxt;
    ll LEFT;
    ll RGHT;
    T (*fnx)(ll,T);
    T (*fab)(T,T);
    LazySegTree1D(ll _l, ll _r, T _v, T (*_fnx)(ll,T), T (*_fab)(T,T))
        :v(1),opr(1),opd(1),nxt(1),LEFT(_l),RGHT(_r),fnx(_fnx),fab(_fab)
    {
        v[0]=fnx(RGHT-LEFT+1,_v);
        opr[0]=0;
        opd[0]=_v;
        nxt[0]=-1;
    }
    void _split(ll i, ll l0, ll r0){
        assert(nxt[i]==-1);
        assert(opr[i]==0);
        assert(l0<r0);
        ll n=v.size();
        nxt[i]=n;
        ll m0=(l0+r0)/2;
        v.push_back(fnx(m0-l0+1,opd[i]));
        v.push_back(fnx(r0-m0,opd[i]));
        opr.push_back(0);
        opr.push_back(0);
        opd.push_back(opd[i]);
        opd.push_back(opd[i]);
        nxt.push_back(-1);
        nxt.push_back(-1);
    }
    void _pushdown(ll i, ll l0, ll r0){
        if (opr[i]==-1) return;
        ll m0=(l0+r0)/2;
        if (nxt[i]==-1)
            _split(i,l0,r0);
        else if (opr[i]>=0){
            _set(nxt[i],l0,m0,l0,m0,opr[i],opd[i]);
            _set(nxt[i]+1,m0+1,r0,m0+1,r0,opr[i],opd[i]);
        }
        opr[i]=-1;
    }
    void _set(ll i, ll l0, ll r0, ll l1, ll r1, ll o1, T v1){
        assert(l1>=l0&&r1<=r0);
        if (l0==l1&&r0==r1){
            if (o1==0){
                v[i]=fnx(r0-l0+1,v1);
                opr[i]=0;opd[i]=v1;
            }else if (o1==1){
                v[i]=fab(v[i],fnx(r0-l0+1,v1));
                if (opr[i]==-1){
                    opr[i]=1;opd[i]=v1;
                }else if (opr[i]==0){
                    opr[i]=0;opd[i]=fab(opd[i],v1);
                }else if (opr[i]==1){
                    opr[i]=1;opd[i]=fab(opd[i],v1);
                }
            }
            return;
        }
        ll m0=(l0+r0)/2;
        _pushdown(i,l0,r0);
        if (l1<=m0) _set(nxt[i],l0,m0,l1,min(r1,m0),o1,v1);
        if (r1>m0) _set(nxt[i]+1,m0+1,r0,max(l1,m0+1),r1,o1,v1);
        v[i]=fab(v[nxt[i]],v[nxt[i]+1]);
    }
    T _get(ll i, ll l0, ll r0, ll l1, ll r1){
        assert(l0<=l1&&r1<=r0);
        if (l0==l1&&r1==r0) return v[i];
        _pushdown(i,l0,r0);
        ll m0=(l0+r0)/2;
        if (r1<=m0) return _get(nxt[i],l0,m0,l1,r1);
        if (m0+1<=l1) return _get(nxt[i]+1,m0+1,r0,l1,r1);
        T lv=_get(nxt[i],l0,m0,l1,m0);
        T rv=_get(nxt[i]+1,m0+1,r0,m0+1,r1);
        return fab(lv,rv);
    }
    void updres(ll l1, ll r1, T v1){_set(0,LEFT,RGHT,l1,r1,1,v1);}
    void setval(ll l1, ll r1, T v1){_set(0,LEFT,RGHT,l1,r1,0,v1);}
    T get(ll l1, ll r1){return _get(0,LEFT,RGHT,l1,r1);}
};

// Suffix array (doubling) O(n*lgn*lgn)
void suffix_array(const string &s, vector<ll> &sa, vector<ll> &rank){
    ll n=s.size();
    ll ri=0;
    vector<vector<ll>> tmprank(2,vector<ll>(n));
    vector<pair<pair<ll,ll>,ll>> kni(n);

    //Compute rank1[] rank2[], rank4[], rank8[]...
    for (ll g=1;g/2<=n-1;g*=2,ri=1-ri){
        for (ll i=0;i<n;i++){
            ll key1=(g==1)?s[i]:tmprank[ri][i];
            ll key2=(g==1)?0:(i+g/2<n)?(tmprank[ri][i+g/2]):-1;
            kni[i]=make_pair(make_pair(key1,key2),i);
        }
        sort(kni.begin(),kni.end());
        for (ll i=0,last_rank=-1;i<n;i++){
            ll this_rank=(i>0&&kni[i].first==kni[i-1].first)?last_rank:i;
            last_rank=tmprank[1-ri][kni[i].second]=this_rank;
        }
    }

    rank.resize(n);rank.assign(tmprank[ri].begin(),tmprank[ri].end());
    sa.resize(n);for (ll i=0;i<n;i++) sa[rank[i]]=i;
}

/**
 * Suffix tree, Ukkonen's algorithm.
 *
 * \usage
 *      string s="qwerty$";
 *      SuffixTree tree(7);//If len of str is know, tell it to prealloc.
 *      for (auto c:s) tree.insert(c);
 */
struct SuffixTree{
    string str;
    vector<unordered_map<ll,ll>> sta;
    vector<unordered_map<ll,ll>> end;
    vector<unordered_map<ll,ll>> nxt;
    vector<ll> suflink;
    ll capacity;
    ll node_count;
    ll act_node;
    ll act_br;
    ll act_len;
    ll todo_count;
    bool _nodehasbr(ll node, ll br){
        auto m=nxt[node];
        return m.find(br)!=m.end();
    }
    ll _newnode(){
        if (node_count<capacity){
            ll newi=node_count++;
            suflink[newi]=-1;
            sta[newi].clear();
            end[newi].clear();
            nxt[newi].clear();
            return newi;
        }
        sta.emplace_back();
        end.emplace_back();
        nxt.emplace_back();
        suflink.push_back(-1);
        ++node_count;
        ++capacity;
        return node_count-1;
    }
    void clear(){
        node_count=0;
        act_node=0;
        act_br=-1;
        act_len=0;
        todo_count=0;
        _newnode();
    }
    SuffixTree(ll est_strlen){
        capacity=0;
        if (est_strlen>0){
            ll est_nodecnt=est_strlen*2;
            capacity=est_nodecnt;
            sta=vector<unordered_map<ll,ll>>(est_nodecnt);
            end=vector<unordered_map<ll,ll>>(est_nodecnt);
            nxt=vector<unordered_map<ll,ll>>(est_nodecnt);
            suflink=vector<ll>(est_nodecnt,-1);
        }
        clear();
    }
    bool _tryextend(ll chr){
        if (act_len==0){
            if (!_nodehasbr(act_node,chr)) return false;
            act_br=chr;
            ++act_len;
            return true;
        }
        //here we do have pending suffixes
        if (act_len==_edgelen(act_node,act_br)){//need to see the nxt node
            ll nxtnode=nxt[act_node][act_br];
            if (!_nodehasbr(nxtnode,chr)) return false;
            act_node=nxtnode;
            act_br=chr;
            act_len=1;
            return true;
        }
        //now act_len<edgelen, we check next char in this edge
        ll start=sta[act_node][act_br];
        if (chr!=str[start+act_len]) return false;
        ++act_len;
        return true;
    }
    void _branch(ll node, ll chr){
        ll newnode=_newnode();
        sta[node][chr]=str.size()-1;
        end[node][chr]=-1;
        nxt[node][chr]=newnode;
    }
    ll _actsplit(ll chr){
        ll new0=_newnode();//for old branch
        ll new1=_newnode();// for new branch

        ll start=sta[act_node][act_br];

        sta[new0][str[start+act_len]]=start+act_len;
        end[new0][str[start+act_len]]=end[act_node][act_br];
        nxt[new0][str[start+act_len]]=nxt[act_node][act_br];
        sta[new0][chr]=str.size()-1;
        end[new0][chr]=-1;
        nxt[new0][chr]=new1;
        sta[act_node][act_br]=start;
        end[act_node][act_br]=start+act_len-1;
        nxt[act_node][act_br]=new0;

        return new0;
    }
    ll _splitorbranch(ll chr){
        if (act_br>=0&&act_len<_edgelen(act_node,act_br)) return _actsplit(chr);
        ll target_node=(act_len==0)?0:nxt[act_node][act_br];
        _branch(target_node,chr);
        return target_node;
    }
    ll _edgerealend(ll node, ll br){
        ll tmp=end[node][br];
        return (tmp==-1)?(str.size()-1):tmp;
    }
    ll _edgelen(ll node, ll br){
        return (br==-1)?0:(!_nodehasbr(node,br))?0:(_edgerealend(node,br)-sta[node][br]+1);
    }
    string _edgestr(ll node, ll br){//get edge string
        ll start=sta[node][br];
        ll end=_edgerealend(node,br);
        return str.substr(start,end-start+1);
    }
    void _actsupdate(ll node, ll br, ll len){act_node=node,act_br=br,act_len=len;}
    void _actfastfwd(ll start, ll slen){//s(start,len): before the new char is inserted
        if (act_br==-1) return;
        while (true){
            ll elen=_edgelen(act_node,act_br);
            if (slen<=elen) return;
            ll new_act_node=nxt[act_node][act_br];
            ll new_act_br=str[start+elen];
            ll new_act_len=act_len-elen;
            _actsupdate(new_act_node,new_act_br,new_act_len);
            start+=elen;
            slen-=elen;
        }
    }
    bool _actsgotonextsuf(){
        if (act_len==0) return false;
        ll estart=sta[act_node][act_br];
        if (act_node==0){
            act_br=(act_len==1)?(-1):str[estart+1];
            --act_len;
            _actfastfwd(estart+1,act_len);
        }else{
            act_node=(suflink[act_node]>=0)?suflink[act_node]:0;
            _actfastfwd(estart,act_len);
        }
    }
    void insert(ll chr){
        str.push_back(chr);
        ++todo_count;
        ll prev_created=-1;
        for (ll waitlink=-1;todo_count>0;--todo_count){
            bool suffix_exists=_tryextend(chr);
            if (suffix_exists){
                waitlink=act_node;
            }else{
                waitlink=_splitorbranch(chr);
            }
            if (prev_created>=0) suflink[prev_created]=waitlink;
            prev_created=waitlink;
            if (suffix_exists) break;
            _actsgotonextsuf();
        }
    }

    pair<ll,ll> calc(ll node){
        if (nxt[node].size()==0) return make_pair(1,0);
        ll leaves=0;
        ll sol=0;
        ll nodesol=0;
        for (auto pr:nxt[node]){
            char br=pr.first;
            ll nxtnode=pr.second;
            ll subleaves,subsol;tie(subleaves,subsol)=calc(nxtnode);
            nodesol+=_edgelen(node,br)*subleaves*(subleaves+1)/2;
            sol+=subsol;
            leaves+=subleaves;
        }
        sol+=nodesol;
        return make_pair(leaves,sol);
    }
};

char buf[100001];
int main()
{
    scanf("%s",buf);
    int sn=strlen(buf);
    SuffixTree tree(sn+1);
    buf[sn]='$';
    for (int i=0;i<=sn;++i){
        tree.insert(buf[i]);
    }
    ll sol=tree.calc(0).second-sn-1;
    cout<<sol<<endl;
    return 0;
}


// template.cpp : Defines the entry point for the console application.
//
#include <climits>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <list>
#include <tuple>
#include <ctime>
#include <cassert>
using namespace std;

//type shortcuts
typedef long long ll;
typedef vector<ll> VI;
typedef long double DOUBLE;
typedef vector<DOUBLE> VD;
typedef vector<VD> VVD;

const ll N=100000;
const ll NN=N*2+2;
char buf[N+1];
ll sta[NN][27];
ll tai[NN][27];
ll nxt[NN][27];
ll strsize;
ll suflink[NN];
ll capacity;
ll node_count;
ll act_node;
ll act_br;
ll act_len;
ll todo_count;
bool _nodehasbr(ll node, ll br){
    return nxt[node][br]>0;
}
ll _newnode(){
    ll newi=node_count++;
    return newi;
}

ll _edgerealend(ll node, ll br){
    ll tmp=tai[node][br];
    return (tmp==-1)?(strsize-1):tmp;
}
ll _edgelen(ll node, ll br){
    return (br==-1)?0:(!_nodehasbr(node,br))?0:(_edgerealend(node,br)-sta[node][br]+1);
}
void clear(){
    strsize=0;
    node_count=0;
    act_node=0;
    act_br=-1;
    act_len=0;
    todo_count=0;
    _newnode();
}
bool _tryextend(ll chr){
    if (act_len==0){
        if (!_nodehasbr(act_node,chr)) return false;
        act_br=chr;
        ++act_len;
        return true;
    }
    //here we do have pending suffixes
    if (act_len==_edgelen(act_node,act_br)){//need to see the nxt node
        ll nxtnode=nxt[act_node][act_br];
        if (!_nodehasbr(nxtnode,chr)) return false;
        act_node=nxtnode;
        act_br=chr;
        act_len=1;
        return true;
    }
    //now act_len<edgelen, we check next char in this edge
    ll start=sta[act_node][act_br];
    if (chr!=buf[start+act_len]) return false;
    ++act_len;
    return true;
}
void _branch(ll node, ll chr){
    ll newnode=_newnode();
    sta[node][chr]=strsize-1;
    tai[node][chr]=-1;
    nxt[node][chr]=newnode;
}
ll _actsplit(ll chr){
    ll new0=_newnode();//for old branch
    ll new1=_newnode();// for new branch

    ll start=sta[act_node][act_br];

    sta[new0][buf[start+act_len]]=start+act_len;
    tai[new0][buf[start+act_len]]=tai[act_node][act_br];
    nxt[new0][buf[start+act_len]]=nxt[act_node][act_br];
    sta[new0][chr]=strsize-1;
    tai[new0][chr]=-1;
    nxt[new0][chr]=new1;
    sta[act_node][act_br]=start;
    tai[act_node][act_br]=start+act_len-1;
    nxt[act_node][act_br]=new0;

    return new0;
}
ll _splitorbranch(ll chr){
    if (act_br>=0&&act_len<_edgelen(act_node,act_br)) return _actsplit(chr);
    ll target_node=(act_len==0)?0:nxt[act_node][act_br];
    _branch(target_node,chr);
    return target_node;
}
void _actsupdate(ll node, ll br, ll len){act_node=node,act_br=br,act_len=len;}
void _actfastfwd(ll start, ll slen){//s(start,len): before the new char is inserted
    if (act_br==-1) return;
    while (true){
        ll elen=_edgelen(act_node,act_br);
        if (slen<=elen) return;
        ll new_act_node=nxt[act_node][act_br];
        ll new_act_br=buf[start+elen];
        ll new_act_len=act_len-elen;
        _actsupdate(new_act_node,new_act_br,new_act_len);
        start+=elen;
        slen-=elen;
    }
}
bool _actsgotonextsuf(){
    if (act_len==0) return false;
    ll estart=sta[act_node][act_br];
    if (act_node==0){
        act_br=(act_len==1)?(-1):buf[estart+1];
        --act_len;
        _actfastfwd(estart+1,act_len);
    }else{
        act_node=(suflink[act_node]>=0)?suflink[act_node]:0;
        _actfastfwd(estart,act_len);
    }
}
void insert(ll chr){
    ++strsize;
    ++todo_count;
    ll prev_created=-1;
    for (ll waitlink=-1;todo_count>0;--todo_count){
        bool suffix_exists=_tryextend(chr);
        if (suffix_exists){
            waitlink=act_node;
        }else{
            waitlink=_splitorbranch(chr);
        }
        if (prev_created>=0) suflink[prev_created]=waitlink;
        prev_created=waitlink;
        if (suffix_exists) break;
        _actsgotonextsuf();
    }
}

pair<ll,ll> calc(ll node){
    ll leaves=0;
    ll sol=0;
    ll nodesol=0;
    for (ll br=0;br<27;br++){
        ll nxtnode=nxt[node][br];
        if (nxtnode==0) continue;
        ll subleaves,subsol;tie(subleaves,subsol)=calc(nxtnode);
        nodesol+=_edgelen(node,br)*subleaves*(subleaves+1)/2;
        sol+=subsol;
        leaves+=subleaves;
    }
    if (leaves==0) return make_pair(1,0);
    sol+=nodesol;
    return make_pair(leaves,sol);
}

int main()
{
    scanf("%s",buf);
    int sn=strlen(buf);
    for (int i=0;i<sn;i++){
        buf[i]-=96;
    }
    clear();
    for (int i=0;i<=sn;++i){
        insert(buf[i]);
    }
    ll sol=calc(0).second-sn-1;
    cout<<sol<<endl;
    return 0;
}


// template.cpp : Defines the entry point for the console application.
//
#include <climits>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <list>
#include <tuple>
#include <ctime>
#include <cassert>
using namespace std;

//type shortcuts
typedef long long ll;
typedef vector<ll> VI;
typedef long double DOUBLE;
typedef vector<DOUBLE> VD;
typedef vector<VD> VVD;

char buf[100001];
const ll N=100000;
const ll NN=N*2+2;
map<ll,ll> sta[NN];
map<ll,ll> tai[NN];
map<ll,ll> nxt[NN];
ll strsize;
ll suflink[NN];
ll capacity;
ll node_count;
ll act_node;
ll act_br;
ll act_len;
ll todo_count;
bool _nodehasbr(ll node, ll br){
    auto m=nxt[node];
    return m.find(br)!=m.end();
}
ll _newnode(){
    ll newi=node_count++;
    suflink[newi]=-1;
    return newi;
}

ll _edgerealend(ll node, ll br){
    ll tmp=tai[node][br];
    return (tmp==-1)?(strsize-1):tmp;
}
ll _edgelen(ll node, ll br){
    return (br==-1)?0:(!_nodehasbr(node,br))?0:(_edgerealend(node,br)-sta[node][br]+1);
}
void clear(){
    strsize=0;
    node_count=0;
    act_node=0;
    act_br=-1;
    act_len=0;
    todo_count=0;
    _newnode();
}
bool _tryextend(ll chr){
    if (act_len==0){
        if (!_nodehasbr(act_node,chr)) return false;
        act_br=chr;
        ++act_len;
        return true;
    }
    //here we do have pending suffixes
    if (act_len==_edgelen(act_node,act_br)){//need to see the nxt node
        ll nxtnode=nxt[act_node][act_br];
        if (!_nodehasbr(nxtnode,chr)) return false;
        act_node=nxtnode;
        act_br=chr;
        act_len=1;
        return true;
    }
    //now act_len<edgelen, we check next char in this edge
    ll start=sta[act_node][act_br];
    if (chr!=buf[start+act_len]) return false;
    ++act_len;
    return true;
}
void _branch(ll node, ll chr){
    ll newnode=_newnode();
    sta[node][chr]=strsize-1;
    tai[node][chr]=-1;
    nxt[node][chr]=newnode;
}
ll _actsplit(ll chr){
    ll new0=_newnode();//for old branch
    ll new1=_newnode();// for new branch

    ll start=sta[act_node][act_br];

    sta[new0][buf[start+act_len]]=start+act_len;
    tai[new0][buf[start+act_len]]=tai[act_node][act_br];
    nxt[new0][buf[start+act_len]]=nxt[act_node][act_br];
    sta[new0][chr]=strsize-1;
    tai[new0][chr]=-1;
    nxt[new0][chr]=new1;
    sta[act_node][act_br]=start;
    tai[act_node][act_br]=start+act_len-1;
    nxt[act_node][act_br]=new0;

    return new0;
}
ll _splitorbranch(ll chr){
    if (act_br>=0&&act_len<_edgelen(act_node,act_br)) return _actsplit(chr);
    ll target_node=(act_len==0)?0:nxt[act_node][act_br];
    _branch(target_node,chr);
    return target_node;
}
void _actsupdate(ll node, ll br, ll len){act_node=node,act_br=br,act_len=len;}
void _actfastfwd(ll start, ll slen){//s(start,len): before the new char is inserted
    if (act_br==-1) return;
    while (true){
        ll elen=_edgelen(act_node,act_br);
        if (slen<=elen) return;
        ll new_act_node=nxt[act_node][act_br];
        ll new_act_br=buf[start+elen];
        ll new_act_len=act_len-elen;
        _actsupdate(new_act_node,new_act_br,new_act_len);
        start+=elen;
        slen-=elen;
    }
}
bool _actsgotonextsuf(){
    if (act_len==0) return false;
    ll estart=sta[act_node][act_br];
    if (act_node==0){
        act_br=(act_len==1)?(-1):buf[estart+1];
        --act_len;
        _actfastfwd(estart+1,act_len);
    }else{
        act_node=(suflink[act_node]>=0)?suflink[act_node]:0;
        _actfastfwd(estart,act_len);
    }
}
void insert(ll chr){
    ++strsize;
    ++todo_count;
    ll prev_created=-1;
    for (ll waitlink=-1;todo_count>0;--todo_count){
        bool suffix_exists=_tryextend(chr);
        if (suffix_exists){
            waitlink=act_node;
        }else{
            waitlink=_splitorbranch(chr);
        }
        if (prev_created>=0) suflink[prev_created]=waitlink;
        prev_created=waitlink;
        if (suffix_exists) break;
        _actsgotonextsuf();
    }
}

pair<ll,ll> calc(ll node){
    if (nxt[node].size()==0) return make_pair(1,0);
    ll leaves=0;
    ll sol=0;
    ll nodesol=0;
    for (auto pr:nxt[node]){
        char br=pr.first;
        ll nxtnode=pr.second;
        ll subleaves,subsol;tie(subleaves,subsol)=calc(nxtnode);
        nodesol+=_edgelen(node,br)*subleaves*(subleaves+1)/2;
        sol+=subsol;
        leaves+=subleaves;
    }
    sol+=nodesol;
    return make_pair(leaves,sol);
}

int main()
{
    scanf("%s",buf);
    int sn=strlen(buf);
    clear();
    buf[sn]='$';
    for (int i=0;i<=sn;++i){
        insert(buf[i]);
    }
    ll sol=calc(0).second-sn-1;
    cout<<sol<<endl;
    return 0;
}


// template.cpp : Defines the entry point for the console application.
//
#include <climits>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <string>
#include <cstring>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <list>
#include <tuple>
#include <ctime>
#include <cassert>
using namespace std;

//type shortcuts
typedef long long ll;
typedef vector<ll> VI;
typedef long double DOUBLE;
typedef vector<DOUBLE> VD;
typedef vector<VD> VVD;

//constants
const DOUBLE EPS=1e-9;
const DOUBLE PI = atan(1) * 4;
const ll M = 1000000007;

//Geometry (real number coordinates)
struct PT {
    double x, y;
    PT() {}
    PT(double x, double y) : x(x), y(y) {}
    PT(const PT &p) : x(p.x), y(p.y) {}
    PT operator + (const PT &p)  const { return PT(x + p.x, y + p.y); }
    PT operator - (const PT &p)  const { return PT(x - p.x, y - p.y); }
    PT operator * (double c)     const { return PT(x*c, y*c); }
    PT operator / (double c)     const { return PT(x / c, y / c); }
};
double dot(PT p, PT q) { return p.x*q.x + p.y*q.y; }
double dist2(PT p, PT q) { return dot(p - q, p - q); }
double cross(PT p, PT q) { return p.x*q.y - p.y*q.x; }
double cos_u2v(const PT& u, const PT& v) { return dot(u, v) / sqrt(dot(u,u)*dot(v,v)); }
double sin_u2v(const PT& u, const PT& v) { return cross(u, v) / sqrt(dot(u, u)*dot(v, v)); }
ostream &operator<<(ostream &os, const PT &p) {
    os << "(" << p.x << "," << p.y << ")";
}
double vector2angle(const PT &v) {return atan2(v.y, v.x);}

//union find
struct UnionFind {
    vector<ll> C;
    UnionFind(ll n) : C(n) {
        for (int i=0;i<n;i++) C[i]=i;
    }
    ll find(ll x) {
        return (C[x] == x) ? x : C[x] = find(C[x]);
    }
    void merge(ll x, ll y) {
        C[find(x)] = find(y);
    }
};

//euclid, modulo
ll mod(ll x, ll m) { return ((x%m) + m) % m; }
ll gcd(ll a, ll b) { for (;b>0;swap(a, b))a %= b;return a; }
ll extended_euclid(ll a, ll b, ll &x, ll &y) {
    ll xx = y = 0;
    ll yy = x = 1;
    while (b) {
        ll q = a / b;
        ll t = b; b = a%b; a = t;
        t = xx; xx = x - q*xx; x = t;
        t = yy; yy = y - q*yy; y = t;
    }
    return a;
}
ll mod_inverse(ll a, ll n) {
    ll x, y;
    ll d = extended_euclid(a, n, x, y);
    if (d > 1) return -1;
    return mod(x, n);
}

//Integer discretization. Return number of values.
ll discretize(vector<ll> &x){
    set<ll> avset(x.begin(),x.end());
    map<ll,ll> vmap;ll dn=0;
    for (auto it=avset.begin();it!=avset.end();it++) vmap[*it]=dn++;
    for (ll i=0,n=x.size();i<n;i++) x[i]=vmap[x[i]];
    return dn;
}

/**
 * Two-phase simplex.
 *
 * \param   A -- m x n matrix
 * \param   b -- m-dim vector
 * \param   c -- n-dim vector
 * \return  x -- an n-dim vector for receiving solution.
 * \return  value (infinity if unbounded above, nan if infeasible)
 *
 * \assume  The problem is:
 *    maximize     c^T x
 *    subject to   Ax <= b
 *                 x >= 0
 *
 * \usage
 *      LPSolver lp=LPSolver(A,b,c);
 *      DOUBLE value=lp.solve(x);
 */
struct LPSolver {
    int m, n;
    VI B, N;
    VVD D;
    LPSolver(const VVD &A, const VD &b, const VD &c) :
        m(b.size()), n(c.size()), N(n + 1), B(m), D(m + 2, VD(n + 2)) {
        for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) D[i][j] = A[i][j];
        for (int i = 0; i < m; i++) { B[i] = n + i; D[i][n] = -1; D[i][n + 1] = b[i]; }
        for (int j = 0; j < n; j++) { N[j] = j; D[m][j] = -c[j]; }
        N[n] = -1; D[m + 1][n] = 1;
    }
    void Pivot(int r, int s) {
        for (int i = 0; i < m + 2; i++) if (i != r)
            for (int j = 0; j < n + 2; j++) if (j != s)
                D[i][j] -= D[r][j] * D[i][s] / D[r][s];
        for (int j = 0; j < n + 2; j++) if (j != s) D[r][j] /= D[r][s];
        for (int i = 0; i < m + 2; i++) if (i != r) D[i][s] /= -D[r][s];
        D[r][s] = 1.0 / D[r][s];
        swap(B[r], N[s]);
    }
    bool Simplex(int phase) {
        int x = phase == 1 ? m + 1 : m;
        while (true) {
            int s = -1;
            for (int j = 0; j <= n; j++) {
                if (phase == 2 && N[j] == -1) continue;
                if (s == -1 || D[x][j] < D[x][s] || D[x][j] == D[x][s] && N[j] < N[s]) s = j;
            }
            if (D[x][s] > -EPS) return true;
            int r = -1;
            for (int i = 0; i < m; i++) {
                if (D[i][s] < EPS) continue;
                if (r == -1 || D[i][n + 1] / D[i][s] < D[r][n + 1] / D[r][s] ||
                    (D[i][n + 1] / D[i][s]) == (D[r][n + 1] / D[r][s]) && B[i] < B[r]) r = i;
            }
            if (r == -1) return false;
            Pivot(r, s);
        }
    }
    DOUBLE solve(VD &x) {
        int r = 0;
        for (int i = 1; i < m; i++) if (D[i][n + 1] < D[r][n + 1]) r = i;
        if (D[r][n + 1] < -EPS) {
            Pivot(r, n);
            if (!Simplex(1) || D[m + 1][n + 1] < -EPS) return -numeric_limits<DOUBLE>::infinity();
            for (int i = 0; i < m; i++) if (B[i] == -1) {
                int s = -1;
                for (int j = 0; j <= n; j++)
                    if (s == -1 || D[i][j] < D[i][s] || D[i][j] == D[i][s] && N[j] < N[s]) s = j;
                Pivot(i, s);
            }
        }
        if (!Simplex(2)) return numeric_limits<DOUBLE>::infinity();
        x = VD(n);
        for (int i = 0; i < m; i++) if (B[i] < n) x[B[i]] = D[i][n + 1];
        return D[m][n + 1];
    }
};

/**
 * Build adjacent lists from edges.
 *
 * \param n     vertex 0 .. n-1
 * \param froms edge.from
 * \param tos   edge.to
 *
 * \assume fromes.len==tos.len
 *
 * \return v2e0 first edge
 * \return e2n  edge.next
 */
void buildadjlist(ll n,
    const vector<ll> &froms, const vector<ll> &tos,
    vector<ll> &v2e0, vector<ll> &e2n)
{
    v2e0.resize(n);
    v2e0.assign(n, -1);
    ll en = froms.size();
    e2n.resize(en);
    for (int i = 0;i < en;i++) {
        ll f = froms[i];
        ll t = tos[i];
        e2n[i] = v2e0[f];
        v2e0[f] = i;
    }
}

//dijkstra
void dijkstra(const vector<ll>& v2e0,
        const vector<ll>& e2t, const vector<ll>& e2c, const vector<ll>& e2n,
        ll src, vector<ll>& dist, vector<ll>& prec)
{
    ll n=v2e0.size();
    dist.resize(n);dist.assign(n,INT_MAX);
    prec.resize(n);prec.assign(n,-1);
    vector<bool> arr(n);
    ll sel=src;
    dist[sel]=0;
    while (sel>=0){
        arr[sel]=true;
        for (ll i=v2e0[sel];i>=0;i=e2n[i]){
            ll to=e2t[i];
            ll cost=e2c[i];
            if (!arr[to]&&dist[to]>dist[sel]+cost){
                dist[to]=dist[sel]+cost;
                prec[to]=sel;
            }
        }
        sel=-1;
        for (ll i=0;i<n;i++)
            if (!arr[i]&&(sel==-1||dist[i]<dist[sel])) sel=i;
    }
}

//Tarjan's strong connected components
void tarjan_dfs(ll v, ll &timer, ll &cn,
        vector<ll> &vtime, vector<ll> &rtime,
        const vector<ll> &v2e0,
        const vector<ll> &e2t, const vector<ll> &e2n,
        vector<ll> &v2c, stack<ll> &stk)
{
    stk.push(v);
    rtime[v]=vtime[v]=timer++;
    for (ll i=v2e0[v];i>=0;i=e2n[i]){
        ll w=e2t[i];
        if (vtime[w]==-1){
            tarjan_dfs(w,timer,cn,vtime,rtime,v2e0,e2t,e2n,v2c,stk);
            rtime[v]=min(rtime[v],rtime[w]);
        }else if (v2c[w]==-1)
            rtime[v]=min(rtime[v],vtime[w]);
    }
    if (vtime[v]==rtime[v]){
        while (true){
            ll pv=stk.top();
            v2c[pv]=cn;
            stk.pop();
            if (pv==v) break;
        }
        cn++;
    }
}
void tarjan_strong(ll n,
        const vector<ll> &v2e0,
        const vector<ll> &e2t,
        const vector<ll> &e2n,
        vector<ll> &v2c)
{
    v2c.assign(n,-1);
    vector<ll> vtime(n,-1);
    vector<ll> rtime(n,-1);
    stack<ll> stk;
    ll timer=0,cn=0;
    for (ll i=0;i<n;i++){
        if (vtime[i]>=0) continue;
        tarjan_dfs(i,timer,cn,vtime,rtime,v2e0,e2t,e2n,v2c,stk);
    }
}

/**
 * Ford Fulkerson max flow algorithm.
 *
 * O(E*maxflow) time.
 * Graph is represented by adjacent lists.
 *
 * \assume source!=sink.
 *
 * \param v2e0  vertex.first_edge
 * \param e2f   edge.from
 * \param e2t   edge.to
 * \param e2c   edge.cost
 * \param e2n   edge.next
 * \param s     source
 * \param t     sink
 *
 * \return  maxflow
 *
 * \usage
 *      FordFulk ff(v2e0,e2f,e2t,e2c,e2n,s,t);
 *      ll sol=ff.eval();
 */
struct FordFulk {
    const vector<ll> &v2e0;
    const vector<ll> &e2f;
    const vector<ll> &e2t;
    const vector<ll> &e2c;
    const vector<ll> &e2n;
    ll s,t;
    FordFulk(const vector<ll> &_v2e0,
        const vector<ll> &_e2f,
        const vector<ll> &_e2t,
        const vector<ll> &_e2c,
        const vector<ll> &_e2n,
        ll _s, ll _t)
        :  s(_s),t(_t),v2e0(_v2e0),e2f(_e2f),e2t(_e2t),e2c(_e2c),e2n(_e2n)
    {}
    ll _find_aug_path(const vector<ll> &v2e0,
        const vector<ll> &e2f,
        const vector<ll> &e2t,
        const vector<ll> &e2c,
        const vector<ll> &e2n,
        ll s, ll t, vector<ll> &epath)
    {
        vector<ll> q2v;
        vector<ll> q2p;
        vector<ll> q2e;
        vector<ll> q2f;
        vector<bool> inq(v2e0.size(), false);
        //BFS
        q2v.push_back(s);
        q2p.push_back(-1);
        q2e.push_back(-1);
        q2f.push_back(INT_MAX);
        inq[s] = true;
        for (ll i = 0;i<q2v.size();i++) {
            ll crt = q2v[i];
            ll f = q2f[i];
            for (ll j = v2e0[crt];j != -1;j = e2n[j]) {
                ll to = e2t[j];
                ll rem = e2c[j];
                if (inq[to]) continue;
                if (rem <= 0) continue;
                q2v.push_back(to);
                q2p.push_back(i);
                q2e.push_back(j);
                q2f.push_back(min(f, rem));
                inq[to] = true;
            }
        }
        //Get Path
        for (ll i = 0;i<q2v.size();i++) {
            if (q2v[i] != t) continue;
            stack<ll> stk;
            for (ll j = i;j>0;j = q2p[j]) {
                stk.push(q2e[j]);
            }
            while (!stk.empty()) {
                epath.push_back(stk.top());
                stk.pop();
            }
            return q2f[i];
        }
        return 0;
    }

    ll eval(){
        //Build residual graph
        ll en = e2f.size();
        map<pair<ll, ll>, ll> rsd;
        for (ll i = 0;i<en;i++) {
            ll f = e2f[i];
            ll t = e2t[i];
            rsd[make_pair(f, t)] += e2c[i];
            rsd[make_pair(t, f)] += 0;
        }
        map<pair<ll, ll>, ll> nei;
        ll ren = 0;
        for (auto er : rsd) {
            nei[er.first] = ren++;
        }
        ll rvn = v2e0.size();
        vector<ll> rv2e0(rvn, -1);
        vector<ll> re2f(ren);//from
        vector<ll> re2t(ren);//to
        vector<ll> re2c(ren);//cost
        vector<ll> re2n(ren);//next edge
        vector<ll> re2p(ren);//peer edge
        ll rei = 0;
        for (auto er : rsd) {
            ll f = er.first.first;
            ll t = er.first.second;
            ll c = er.second;
            re2f[rei] = f;
            re2t[rei] = t;
            re2c[rei] = c;
            re2p[rei] = nei[make_pair(t, f)];
            re2n[rei] = rv2e0[f];
            rv2e0[f] = rei;
            ++rei;
        }

        //FF main
        ll sol = 0;
        vector<ll> epath;
        while (true) {
            epath.clear();
            ll res = _find_aug_path(rv2e0, re2f, re2t, re2c, re2n, s, t, epath);
            if (res == 0) break;
            for (auto ei : epath) {
                re2c[ei] -= res;
                re2c[re2p[ei]] += res;
            }
            sol += res;
        }
        return sol;
    }
};

// 1D Binary indexed tree.
// Point update(by relative diff) & [0..k] interval query.
template <typename T>
struct BIT1D {
    ll n;
    vector<T> v;
    T (*fnx)(ll,T);
    T (*fab)(T,T);
    ll rg(ll k){return ((k^(k+1))+1)/2;}
    BIT1D (ll _n, T v0, T (*_fnx)(ll,T), T (*_fab)(T,T))
            :n(_n),v(_n),fnx(_fnx),fab(_fab)
    {for (ll i=0;i<n;i++) v[i]=fnx(rg(i),v0);}
    void update(ll i, T v1){
        for (;i<n;i+=rg(i)) v[i]=fab(v[i],v1);
    }
    T query(ll i){
        ll j=i-rg(i);
        return (j<0)?v[i]:(query(j)+v[i]);
    }
};

/**
 * 1D generic segment tree. Lazy propgation & on-demand allocation.
 *
 * Allow 1 type of segmentable interval query(SUM/MAX),
 *       interval update by relative diff(each+1/each-lift-to-5), and
 *       interval update by value-setting(each-set-to-9)
 *
 * \param _l    left-most position
 * \param _r    right-most position
 * \param _v    initial element value
 * \param _fnx  query function I    (compute X+X+X+...+X)
 * \param _fab  query function II   (compute A+B)
 *
 * \usage
 *      double max_ab(double a, double b){return max(a,b);}
 *      double max_nx(int n,double x){return x;}
 *      LazySegTree1D<R> t(0,99,0.0,max_nx,max_ab);//query MAX(seg) on [0,99]
 *      tree.updres(1,6,2.2); //lift each to 2.2 on [1,6]
 *      tree.setval(0,5,1.1); //set each to 1.1 on [0,5]
 *      auto v=tree.get(2,7); // get maximum on [2,7]
 */
template <typename T>
struct LazySegTree1D {
    vector<T> v;
    vector<ll> opr;//0: set value; 1: update result; -1: idle
    vector<T> opd;
    vector<ll> nxt;
    ll LEFT;
    ll RGHT;
    T (*fnx)(ll,T);
    T (*fab)(T,T);
    LazySegTree1D(ll _l, ll _r, T _v, T (*_fnx)(ll,T), T (*_fab)(T,T))
        :v(1),opr(1),opd(1),nxt(1),LEFT(_l),RGHT(_r),fnx(_fnx),fab(_fab)
    {
        v[0]=fnx(RGHT-LEFT+1,_v);
        opr[0]=0;
        opd[0]=_v;
        nxt[0]=-1;
    }
    void _split(ll i, ll l0, ll r0){
        assert(nxt[i]==-1);
        assert(opr[i]==0);
        assert(l0<r0);
        ll n=v.size();
        nxt[i]=n;
        ll m0=(l0+r0)/2;
        v.push_back(fnx(m0-l0+1,opd[i]));
        v.push_back(fnx(r0-m0,opd[i]));
        opr.push_back(0);
        opr.push_back(0);
        opd.push_back(opd[i]);
        opd.push_back(opd[i]);
        nxt.push_back(-1);
        nxt.push_back(-1);
    }
    void _pushdown(ll i, ll l0, ll r0){
        if (opr[i]==-1) return;
        ll m0=(l0+r0)/2;
        if (nxt[i]==-1)
            _split(i,l0,r0);
        else if (opr[i]>=0){
            _set(nxt[i],l0,m0,l0,m0,opr[i],opd[i]);
            _set(nxt[i]+1,m0+1,r0,m0+1,r0,opr[i],opd[i]);
        }
        opr[i]=-1;
    }
    void _set(ll i, ll l0, ll r0, ll l1, ll r1, ll o1, T v1){
        assert(l1>=l0&&r1<=r0);
        if (l0==l1&&r0==r1){
            if (o1==0){
                v[i]=fnx(r0-l0+1,v1);
                opr[i]=0;opd[i]=v1;
            }else if (o1==1){
                v[i]=fab(v[i],fnx(r0-l0+1,v1));
                if (opr[i]==-1){
                    opr[i]=1;opd[i]=v1;
                }else if (opr[i]==0){
                    opr[i]=0;opd[i]=fab(opd[i],v1);
                }else if (opr[i]==1){
                    opr[i]=1;opd[i]=fab(opd[i],v1);
                }
            }
            return;
        }
        ll m0=(l0+r0)/2;
        _pushdown(i,l0,r0);
        if (l1<=m0) _set(nxt[i],l0,m0,l1,min(r1,m0),o1,v1);
        if (r1>m0) _set(nxt[i]+1,m0+1,r0,max(l1,m0+1),r1,o1,v1);
        v[i]=fab(v[nxt[i]],v[nxt[i]+1]);
    }
    T _get(ll i, ll l0, ll r0, ll l1, ll r1){
        assert(l0<=l1&&r1<=r0);
        if (l0==l1&&r1==r0) return v[i];
        _pushdown(i,l0,r0);
        ll m0=(l0+r0)/2;
        if (r1<=m0) return _get(nxt[i],l0,m0,l1,r1);
        if (m0+1<=l1) return _get(nxt[i]+1,m0+1,r0,l1,r1);
        T lv=_get(nxt[i],l0,m0,l1,m0);
        T rv=_get(nxt[i]+1,m0+1,r0,m0+1,r1);
        return fab(lv,rv);
    }
    void updres(ll l1, ll r1, T v1){_set(0,LEFT,RGHT,l1,r1,1,v1);}
    void setval(ll l1, ll r1, T v1){_set(0,LEFT,RGHT,l1,r1,0,v1);}
    T get(ll l1, ll r1){return _get(0,LEFT,RGHT,l1,r1);}
};

// Suffix array (doubling) O(n*lgn*lgn)
void suffix_array(const string &s, vector<ll> &sa, vector<ll> &rank){
    ll n=s.size();
    ll ri=0;
    vector<vector<ll>> tmprank(2,vector<ll>(n));
    vector<pair<pair<ll,ll>,ll>> kni(n);

    //Compute rank1[] rank2[], rank4[], rank8[]...
    for (ll g=1;g/2<=n-1;g*=2,ri=1-ri){
        for (ll i=0;i<n;i++){
            ll key1=(g==1)?s[i]:tmprank[ri][i];
            ll key2=(g==1)?0:(i+g/2<n)?(tmprank[ri][i+g/2]):-1;
            kni[i]=make_pair(make_pair(key1,key2),i);
        }
        sort(kni.begin(),kni.end());
        for (ll i=0,last_rank=-1;i<n;i++){
            ll this_rank=(i>0&&kni[i].first==kni[i-1].first)?last_rank:i;
            last_rank=tmprank[1-ri][kni[i].second]=this_rank;
        }
    }

    rank.resize(n);rank.assign(tmprank[ri].begin(),tmprank[ri].end());
    sa.resize(n);for (ll i=0;i<n;i++) sa[rank[i]]=i;
}

/**
 * Suffix tree, Ukkonen's algorithm.
 *
 * \usage
 *      string s="qwerty$";
 *      SuffixTree tree(7);//If len of str is know, tell it to prealloc.
 *      for (auto c:s) tree.insert(c);
 */
struct SuffixTree{
    string str;
    vector<unordered_map<ll,ll>> sta;
    vector<unordered_map<ll,ll>> end;
    vector<unordered_map<ll,ll>> nxt;
    vector<ll> suflink;
    ll capacity;
    ll node_count;
    ll act_node;
    ll act_br;
    ll act_len;
    ll todo_count;
    bool _nodehasbr(ll node, ll br){
        auto m=nxt[node];
        return m.find(br)!=m.end();
    }
    ll _newnode(){
        if (node_count<capacity){
            ll newi=node_count++;
            suflink[newi]=-1;
            sta[newi].clear();
            end[newi].clear();
            nxt[newi].clear();
            return newi;
        }
        sta.emplace_back();
        end.emplace_back();
        nxt.emplace_back();
        suflink.push_back(-1);
        ++node_count;
        ++capacity;
        return node_count-1;
    }
    void clear(){
        node_count=0;
        act_node=0;
        act_br=-1;
        act_len=0;
        todo_count=0;
        _newnode();
    }
    SuffixTree(ll est_strlen){
        capacity=0;
        if (est_strlen>0){
            ll est_nodecnt=est_strlen*2;
            capacity=est_nodecnt;
            sta=vector<unordered_map<ll,ll>>(est_nodecnt);
            end=vector<unordered_map<ll,ll>>(est_nodecnt);
            nxt=vector<unordered_map<ll,ll>>(est_nodecnt);
            suflink=vector<ll>(est_nodecnt,-1);
        }
        clear();
    }
    bool _tryextend(ll chr){
        if (act_len==0){
            if (!_nodehasbr(act_node,chr)) return false;
            act_br=chr;
            ++act_len;
            return true;
        }
        //here we do have pending suffixes
        if (act_len==_edgelen(act_node,act_br)){//need to see the nxt node
            ll nxtnode=nxt[act_node][act_br];
            if (!_nodehasbr(nxtnode,chr)) return false;
            act_node=nxtnode;
            act_br=chr;
            act_len=1;
            return true;
        }
        //now act_len<edgelen, we check next char in this edge
        ll start=sta[act_node][act_br];
        if (chr!=str[start+act_len]) return false;
        ++act_len;
        return true;
    }
    void _branch(ll node, ll chr){
        ll newnode=_newnode();
        sta[node][chr]=str.size()-1;
        end[node][chr]=-1;
        nxt[node][chr]=newnode;
    }
    ll _actsplit(ll chr){
        ll new0=_newnode();//for old branch
        ll new1=_newnode();// for new branch

        ll start=sta[act_node][act_br];

        sta[new0][str[start+act_len]]=start+act_len;
        end[new0][str[start+act_len]]=end[act_node][act_br];
        nxt[new0][str[start+act_len]]=nxt[act_node][act_br];
        sta[new0][chr]=str.size()-1;
        end[new0][chr]=-1;
        nxt[new0][chr]=new1;
        sta[act_node][act_br]=start;
        end[act_node][act_br]=start+act_len-1;
        nxt[act_node][act_br]=new0;

        return new0;
    }
    ll _splitorbranch(ll chr){
        if (act_br>=0&&act_len<_edgelen(act_node,act_br)) return _actsplit(chr);
        ll target_node=(act_len==0)?0:nxt[act_node][act_br];
        _branch(target_node,chr);
        return target_node;
    }
    ll _edgerealend(ll node, ll br){
        ll tmp=end[node][br];
        return (tmp==-1)?(str.size()-1):tmp;
    }
    ll _edgelen(ll node, ll br){
        return (br==-1)?0:(!_nodehasbr(node,br))?0:(_edgerealend(node,br)-sta[node][br]+1);
    }
    string _edgestr(ll node, ll br){//get edge string
        ll start=sta[node][br];
        ll end=_edgerealend(node,br);
        return str.substr(start,end-start+1);
    }
    void _actsupdate(ll node, ll br, ll len){act_node=node,act_br=br,act_len=len;}
    void _actfastfwd(ll start, ll slen){//s(start,len): before the new char is inserted
        if (act_br==-1) return;
        while (true){
            ll elen=_edgelen(act_node,act_br);
            if (slen<=elen) return;
            ll new_act_node=nxt[act_node][act_br];
            ll new_act_br=str[start+elen];
            ll new_act_len=act_len-elen;
            _actsupdate(new_act_node,new_act_br,new_act_len);
            start+=elen;
            slen-=elen;
        }
    }
    bool _actsgotonextsuf(){
        if (act_len==0) return false;
        ll estart=sta[act_node][act_br];
        if (act_node==0){
            act_br=(act_len==1)?(-1):str[estart+1];
            --act_len;
            _actfastfwd(estart+1,act_len);
        }else{
            act_node=(suflink[act_node]>=0)?suflink[act_node]:0;
            _actfastfwd(estart,act_len);
        }
    }
    void insert(ll chr){
        str.push_back(chr);
        ++todo_count;
        ll prev_created=-1;
        for (ll waitlink=-1;todo_count>0;--todo_count){
            bool suffix_exists=_tryextend(chr);
            if (suffix_exists){
                waitlink=act_node;
            }else{
                waitlink=_splitorbranch(chr);
            }
            if (prev_created>=0) suflink[prev_created]=waitlink;
            prev_created=waitlink;
            if (suffix_exists) break;
            _actsgotonextsuf();
        }
    }

    pair<ll,ll> calc(ll node){
        if (nxt[node].size()==0) return make_pair(1,0);
        int leaves=0;
        int sol=0;
        int nodesol=0;
        for (auto pr:nxt[node]){
            char br=pr.first;
            ll nxtnode=pr.second;
            ll subleaves,subsol;tie(subleaves,subsol)=calc(nxtnode);
            nodesol+=_edgelen(node,br)*subleaves*(subleaves+1)/2;
            sol+=subsol;
            leaves+=subleaves;
        }
        sol+=nodesol;
        return make_pair(leaves,sol);
    }
};

char buf[100001];
int main()
{
    scanf("%s",buf);
    int sn=strlen(buf);
    SuffixTree tree(sn+1);
    buf[sn]='$';
    for (int i=0;i<=sn;++i){
        tree.insert(buf[i]);
    }
    ll sol=tree.calc(0).second-sn-1;
    cout<<sol<<endl;
    return 0;
}


#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 100002;
const int MAXM = 20;

int n, m, k;
char s[MAXN];
int cnt[MAXN];
int A[MAXM][MAXN];
int p[MAXN], _p[MAXN], a[MAXN], _a[MAXN];

int h[MAXN], w[MAXN];
long long f[MAXN];

int sorting(int st, int &k){
    for(int i = 0; i < n; i++)
        _p[i] = p[i] - st < 0? p[i] - st + n : p[i] - st;
    for(int i = 0; i < k; i++)
        cnt[i] = 0;
    for(int i = 0; i < n; i++)
        cnt[a[_p[i]]]++;
    for(int i = 1; i < k; i++)
        cnt[i] += cnt[i - 1];
    for(int i = n - 1; i >= 0; i--)
        p[--cnt[a[_p[i]]]] = _p[i];
    for (int i = 0; i < n; i++) _p[i] = p[i] + st < n? p[i] + st : p[i] + st - n;

    _a[p[0]] = 0;
    k = 1;
    fo (int i = 1; i < n; i++){
        if (a[p[i - 1]] != a[p[i]] || a[_p[i - 1]] != a[_p[i]]) k++;
        _a[p[i]] = k - 1;
    }
    for(int i = 0; i < n; i++) a[i] = _a[i];
    return 0;
}

int lcp(int x, int y){
    int res = 0;
    for(int i = m - 1; i >= 0; i--)
        if(A[i][x] == A[i][y]){
            x += 1 << i;
            y += 1 << i;
            res += 1 << i;
        }
    return res;
}

int main(){
    gets(s);
    n = strlen(s);
    long long ans = (long long)n * (n + 1) / 2;
    s[n++] = 0;
    for(int i = 0; i < n; i++){
        p[i] = i;
        a[i] = s[i];
    }
    k = 256;
    sorting(0, k);
    for(m = 0; (1 << m) < n; m++){
        int st = 1 << m;
        for (int i = 0; i < n; i++)
            A[m][i] = a[i];
        sorting(st, k);
    }
    for (int i = 0; i < n; i++)
        A[m][i] = a[i];
    m++;
    k = 0;
    for(int i = 2; i < n; i++)
        a[k++] = lcp(p[i - 1], p[i]);
    n = 0;
    h[n] = -1;
    w[n] = 0;
    f[n] = 0;
    n++;
    for(int i = 0; i < k; i++){
        int l = 1;
        while (h[n - 1] >= a[i]) l += w[--n];
        w[n] = l;
        h[n] = a[i];
        f[n] = f[n - 1] + (long long)l * a[i];
        n++;
        ans += f[n - 1];
    }
    printf("%I64d\n", ans);
    return 0;
}


#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 100002;
const int MAXM = 20;

int n, m, k;
char s[MAXN];
int cnt[MAXN];
int A[MAXM][MAXN];
int p[MAXN], _p[MAXN], a[MAXN], _a[MAXN];

int h[MAXN], w[MAXN];
long long f[MAXN];

int sorting(int st, int &k){
    for(int i = 0; i < n; i++)
        _p[i] = p[i] - st < 0? p[i] - st + n : p[i] - st;
    for(int i = 0; i < k; i++)
        cnt[i] = 0;
    for(int i = 0; i < n; i++)
        cnt[a[_p[i]]]++;
    for(int i = 1; i < k; i++)
        cnt[i] += cnt[i - 1];
    for(int i = n - 1; i >= 0; i--)
        p[--cnt[a[_p[i]]]] = _p[i];
    for (int i = 0; i < n; i++) _p[i] = p[i] + st < n? p[i] + st : p[i] + st - n;

    _a[p[0]] = 0;
    k = 1;
    for(int i = 1; i < n; i++){
        if (a[p[i - 1]] != a[p[i]] || a[_p[i - 1]] != a[_p[i]]) k++;
        _a[p[i]] = k - 1;
    }
    for(int i = 0; i < n; i++) a[i] = _a[i];
    return 0;
}

int lcp(int x, int y){
    int res = 0;
    for(int i = m - 1; i >= 0; i--)
        if(A[i][x] == A[i][y]){
            x += 1 << i;
            y += 1 << i;
            res += 1 << i;
        }
    return res;
}

int main(){
    gets(s);
    n = strlen(s);
    long long ans = (long long)n * (n + 1) / 2;
    s[n++] = 0;
    for(int i = 0; i < n; i++){
        p[i] = i;
        a[i] = s[i];
    }
    k = 256;
    sorting(0, k);
    for(m = 0; (1 << m) < n; m++){
        int st = 1 << m;
        for (int i = 0; i < n; i++)
            A[m][i] = a[i];
        sorting(st, k);
    }
    for (int i = 0; i < n; i++)
        A[m][i] = a[i];
    m++;
    k = 0;
    for(int i = 2; i < n; i++)
        a[k++] = lcp(p[i - 1], p[i]);
    n = 0;
    h[n] = -1;
    w[n] = 0;
    f[n] = 0;
    n++;
    for(int i = 0; i < k; i++){
        int l = 1;
        while (h[n - 1] >= a[i]) l += w[--n];
        w[n] = l;
        h[n] = a[i];
        f[n] = f[n - 1] + (long long)l * a[i];
        n++;
        ans += f[n - 1];
    }
    printf("%I64d\n", ans);
    return 0;
}


# include <bits/stdc++.h>

/*
// ordered_set
# include <ext/pb_ds/assoc_container.hpp>
# include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
# define ordered_set tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update>
*/

using namespace std;

typedef long long                                        ll;
typedef long double                                      ld;
typedef pair <int, int>                                  pii;
typedef pair <pii, int>                                  ppi;
typedef pair <int, pii>                                  pip;
typedef pair <pii, pii>                                  ppp;
typedef pair <ll, ll>                                    pll;

# define A                                               first
# define B                                               second
# define endl                                            '\n'
# define sep                                             ' '
# define all(x)                                          x.begin(), x.end()
# define kill(x)                                         return cout << x << endl, 0
# define SZ(x)                                           int(x.size())
# define lc                                              id << 1
# define rc                                              id << 1 | 1
# define InTheNameOfGod                                  ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);

ll power(ll a, ll b, ll md) {return (!b ? 1 : (b & 1 ? a * power(a * a % md, b / 2, md) % md : power(a * a % md, b / 2, md) % md));}

const int xn = 1e5 + 10;
const int xm = 10 + 10;
const int sq = 320;
const int inf = 1e9 + 10;
const ll INF = 1e18 + 10;
const int mod = 998244353;
const int base = 257;

int n, Rank[xm][xn], pw, lcp[xn], P[xn];
int L[xn], R[xn];
stack <int> st;
ll ans;
string S;

bool cmp(int i, int j){
	if (Rank[pw - 1][i] != Rank[pw - 1][j])
		return Rank[pw - 1][i] < Rank[pw - 1][j];
	if (max(i, j) + (1 << (pw - 1)) > n)
		return i > j;
	return Rank[pw - 1][(1 << (pw - 1)) + i] < Rank[pw - 1][(1 << (pw - 1)) + j];
}
void buildSA(){
	for (int i = 1; i <= n; ++ i)
		Rank[0][i] = S[i - 1], P[i] = i;
	for (pw = 1; pw < xm; ++ pw){
		sort(P + 1, P + n + 1, cmp);
		Rank[pw][P[1]] = 1;
		for (int i = 2; i <= n; ++ i)
			Rank[pw][P[i]] = Rank[pw][P[i - 1]] + cmp(P[i - 1], P[i]);
	}
}
int LCP(int x, int y){
	int res = 0;
	for (int i = xm - 1; i >= 0; -- i){
		if (Rank[i][x] != Rank[i][y])
			continue;
		x += 1 << i, y += 1 << i;
		res |= (1 << i);
	}
	return res;
}

int main(){
	InTheNameOfGod;

	cin >> S;
	n = SZ(S);
	buildSA();
	for (int i = 1; i < n; ++ i)
		lcp[i] = LCP(P[i], P[i + 1]);
	while (SZ(st))
		st.pop();
	for (int i = 1; i < n; ++ i){
		while (SZ(st) && lcp[st.top()] >= lcp[i])
			st.pop();
		L[i] = 0;
		if (SZ(st))
			L[i] = st.top();
		st.push(i);
	}
	while (SZ(st))
		st.pop();
	for (int i = n - 1; i >= 1; -- i){
		while (SZ(st) && lcp[st.top()] > lcp[i])
			st.pop();
		R[i] = n;
		if (SZ(st))
			R[i] = st.top();
		st.push(i);
	}
	ans = ll(n) * ll(n + 1) / 2;
	for (int i = 1; i < n; ++ i){
		int x = i - L[i], y = R[i] - i;
		ans += ll(x) * ll(y) * ll(lcp[i]);
	}
	cout << ans << endl;

	return 0;
}


// In the name of God

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define Size(x) ((int)(x).size())
typedef long long ll;
typedef long double ld;
typedef pair<int , int>pii;
const int INF = 1e9 + 10 , MN = 3e5 + 10;

int sz=1 , lst , nxt[26][MN] , A[MN] , B[MN] , link[MN] , len[MN];
bool mk[MN];
vector<int>topol , adj[MN] , rev[MN];

void dfs(int s){
	mk[s] = true;
	for(auto v:adj[s]) if(!mk[v])
		dfs(v);
	topol.pb(s);
}

void add(int c){
	int p = lst;
	lst =sz++;
	len[lst] = len[p]+1;
	for(;!nxt[c][p];p=link[p])
		nxt[c][p] = lst;
	int q = nxt[c][p];
	if(q == lst){
		link[lst] = p;
		return;
	}
	if(len[q] = len[p]+1){
		link[lst] = q;
		return;
	}
	int clone = sz++;
	link[clone] = link[q];
	for(int i=0;i<26;++i) nxt[i][clone] = nxt[i][q];
	link[lst] = link[q] = clone;
	for(;nxt[c][p]==q;p=link[p])
		nxt[c][p] = clone;
}

int main(){
	ios_base :: sync_with_stdio(false) , cin.tie(0) , cout.tie(0);
	string s;
	cin >> s;
	for(int i=0;i<Size(s);++i) add(s[i]-'a');
	for(int i=0;i<sz;++i)
		for(int j=0;j<26;++j) if(nxt[j][i]){
			adj[i].pb(nxt[j][i]);
			rev[nxt[j][i]].pb(i);
		}

	for(int i=0;i<sz;++i) if(!mk[i])
		dfs(i);
	memset(mk , 0 , sizeof mk);
	int cur = lst;
	while(cur) mk[cur] = true , cur = link[cur];

	for(auto v:topol){
		B[v] = mk[v];
		for(auto u:adj[v])
			B[v] += B[u];
	}

	reverse(topol.begin() , topol.end());

	A[0] = 1;
	for(auto v:topol)
		for(auto u:rev[v])
			A[v] += A[u];

	ll ans = 0;
	for(int i=1;i<sz;++i)
		ans += 1ll * A[i] * (1ll * B[i] * (B[i]+1) / 2);
	cout << ans << '\n';

	return 0;
}


// In the name of God

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define Size(x) ((int)(x).size())
typedef long long ll;
typedef long double ld;
typedef pair<int , int>pii;
const int INF = 1e9 + 10 , MN = 3e5 + 10;

int sz=1 , lst , nxt[26][MN] , A[MN] , B[MN] , link[MN] , len[MN];
bool mk[MN];
vector<int>topol , adj[MN] , rev[MN];

void dfs(int s){
	mk[s] = true;
	for(auto v:adj[s]) if(!mk[v])
		dfs(v);
	topol.pb(s);
}

void add(int c){
	int p = lst;
	lst =sz++;
	len[lst] = len[p]+1;
	for(;!nxt[c][p];p=link[p])
		nxt[c][p] = lst;
	int q = nxt[c][p];
	if(q == lst){
		link[lst] = p;
		return;
	}
	if(len[q] == len[p]+1){
		link[lst] = q;
		return;
	}
	int clone = sz++;
	len[clone] = len[p]+1;
	link[clone] = link[q];
	for(int i=0;i<26;++i) nxt[i][clone] = nxt[i][q];
	link[lst] = link[q] = clone;
	for(;nxt[c][p]==q;p=link[p])
		nxt[c][p] = clone;
}

int main(){
	ios_base :: sync_with_stdio(false) , cin.tie(0) , cout.tie(0);
	string s;
	cin >> s;
	for(int i=0;i<Size(s);++i){
		add(s[i]-'a');

/*		for(int i=0;i<sz;++i){
			for(int j=0;j<26;++j) if(nxt[j][i])
				cout << i << ' ' << char('a'+j) << ' ' << nxt[j][i] << '\n';
		}
		for(int i=0;i<sz;++i) cout << i << ' ' << link[i] << "***\n";
		cout << "LAST IS " << lst << '\n';
		cout << "-------\n";
*/	}
	for(int i=0;i<sz;++i)
		for(int j=0;j<26;++j) if(nxt[j][i]){
			adj[i].pb(nxt[j][i]);
			rev[nxt[j][i]].pb(i);
		}

	for(int i=0;i<sz;++i) if(!mk[i])
		dfs(i);
	memset(mk , 0 , sizeof mk);
	int cur = lst;
	while(cur) mk[cur] = true , cur = link[cur];

	for(auto v:topol){
		B[v] = mk[v];
		for(auto u:adj[v])
			B[v] += B[u];
	}

	reverse(topol.begin() , topol.end());

	A[0] = 1;
	for(auto v:topol)
		for(auto u:rev[v])
			A[v] += A[u];

	ll ans = 0;
	for(int i=1;i<sz;++i)
		ans += 1ll * A[i] * B[i] * (B[i]+1)/2;
	cout << ans << '\n';
	return 0;
}


// #pragma GCC optimize("Ofast")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
// #pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define FAST std::ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define DECIMAL(n)  std::cout << std::fixed;std::cout << std::setprecision(n);
#define hell 1000000007
#define narak 998244353
#define PI 3.14159265358979323844
#define mp make_pair
#define eb emplace_back
#define pb push_back
#define fi first
#define se second
#define all(v) v.begin(), v.end()
#define endl "\n"
#define sqr(a) (a)*(a)
#define __lcm(m,n) m*(n/__gcd(m,n))
#define inputarr(a,n) for(int xxx=0;xxx<n;++xxx) cin>>(a)[xxx]
#define sz(a) (int)a.size()
#define sl(a) (int)a.length()
#define display(x) for(auto &a:x) cout<<a<<" ";cout<<endl
#define debug cerr<<"bhau"<<endl
#define between(n,a,b) (a<=n&&n<=b)
#define elasped_time 1.0 * clock() / CLOCKS_PER_SEC

#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
    std::cerr << name << " : " << arg1 << endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
    const char* comma = strchr(names + 1, ',');std::cerr.write(names, comma - names) << " : " << arg1<<" | ";__f(comma+1, args...);
}

template<typename T, typename U> static inline void amin(T &x, U y) {
    if (y < x)
        x = y;
}

template<typename T, typename U> static inline void amax(T &x, U y) {
    if (x < y)
        x = y;
}

template<typename T,typename U> std::ostream& operator<<(std::ostream& out, std::pair<T,U> a) {
    out<<a.fi<<" "<<a.se;
    return out;
}

template<typename T,typename U> std::istream& operator>>(std::istream& in, std::pair<T,U> &a) {
    in>>a.fi>>a.se;
    return in;
}

inline long long toint(const std::string &s) {std::stringstream ss; ss << s; long long x; ss >> x; return x;}
inline std::string tostring(long long number) {std::stringstream ss; ss << number; return ss.str();}
inline std::string tobin(long long x) {return std::bitset<63>(x).to_string();}

std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());

#define ll long long
#define pii std::pair<int, int>
#define vi vector<int>
#define vvi vector<vi >
#define vii vector<pii >
#define point complex<ll>
#define ordered_set tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update>

/*----------------------Graph Moves----------------*/
//const int fx[]={+1,-1,+0,+0};
//const int fy[]={+0,+0,+1,-1};
//const int fx[]={+0,+0,+1,-1,-1,+1,-1,+1};   // Kings Move
//const int fy[]={-1,+1,+0,+0,+1,+1,-1,-1};  // Kings Move
//const int fx[]={-2, -2, -1, -1,  1,  1,  2,  2};  // Knights Move
//const int fy[]={-1,  1, -2,  2, -2,  2, -1,  1}; // Knights Move
/*------------------------------------------------*/

//primes for hashing 937,991,1013,1409,1741

using namespace std;
using namespace __gnu_pbds;
struct state{
    int len,link;
    int next[26];
};
const int maxn=100005;
struct SuffixAutomaton{
    state st[2*maxn];
    int size,last;
    ll ans;
    ll cnt[2*maxn],words[2*maxn];
    SuffixAutomaton(){
        st[0].len=0;
        st[0].link=-1;
        size=1;
        last=0;
        memset(words,0,sizeof(words));
        words[0]=1;
    }
    void extend(char c){
        int cur=size++;
        st[cur].len=st[last].len+1;
        int p=last;
        while(p!=-1&&!st[p].next[c]){
            st[p].next[c]=cur;
            words[cur]+=words[p];
            p=st[p].link;
        }
        if(p==-1){
            st[cur].link=0;
        }else{
            int q=st[p].next[c];
            if(st[p].len+1==st[q].len){
                st[cur].link=q;
            }else{
                int clone=size++;
                st[clone].len=st[p].len+1;
                for (int i = 0; i < 26; ++i) {
                    st[clone].next[i]=st[q].next[i];
                }
                st[clone].link=st[q].link;
                while(p!=-1&&st[p].next[c]==q){
                    st[p].next[c]=clone;
                    words[clone]+=words[p];
                    words[q]-=words[p];
                    p=st[p].link;
                }
                st[q].link=st[cur].link=clone;
            }
        }
        last=cur;
    }
    bool vis[2*maxn];
    bool term[2*maxn];
    void dfs(int v){
        if(vis[v])return ;
        vis[v]=1;
        cnt[v]=term[v];
        for (int i = 0; i < 26; ++i) {
            if(st[v].next[i]){
                dfs(st[v].next[i]);
                cnt[v]+=cnt[st[v].next[i]];
            }
        }
    }
    void build(string &s){
        for(auto &c:s){
            extend(c-'a');
        }
        memset(cnt,0,sizeof(cnt));
        memset(vis,0,sizeof(vis));
        memset(term,0,sizeof(term));
        int cur=last;
        term[cur]=1;
        while(cur){
            cur=st[cur].link;
            term[cur]=1;
        }
        dfs(0);
        for (int i = 1; i < size; ++i) {
            //trace(i,cnt[i],words[i]);
            ans+=(cnt[i]*(cnt[i]+1)/2)*words[i];
        }
    }
};
void meowmeow321()
{
    string s;
    cin>>s;
    SuffixAutomaton sa;
    sa.build(s);
    cout<<sa.ans<<endl;
}
signed main()
{
    FAST;
    int testcases=1;
    //cin>>testcases;
    for(int i=0;i<testcases;++i)
    {
        meowmeow321();
    }
    cerr<<endl<<"Time Elasped : "<<elasped_time<<endl;
    return 0;
}


// #pragma GCC optimize("Ofast")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
// #pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define FAST std::ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define DECIMAL(n)  std::cout << std::fixed;std::cout << std::setprecision(n);
#define hell 1000000007
#define narak 998244353
#define PI 3.14159265358979323844
#define mp make_pair
#define eb emplace_back
#define pb push_back
#define fi first
#define se second
#define all(v) v.begin(), v.end()
#define endl "\n"
#define sqr(a) (a)*(a)
#define __lcm(m,n) m*(n/__gcd(m,n))
#define inputarr(a,n) for(int xxx=0;xxx<n;++xxx) cin>>(a)[xxx]
#define sz(a) (int)a.size()
#define sl(a) (int)a.length()
#define display(x) for(auto &a:x) cout<<a<<" ";cout<<endl
#define debug cerr<<"bhau"<<endl
#define between(n,a,b) (a<=n&&n<=b)
#define elasped_time 1.0 * clock() / CLOCKS_PER_SEC

#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1){
    std::cerr << name << " : " << arg1 << endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args){
    const char* comma = strchr(names + 1, ',');std::cerr.write(names, comma - names) << " : " << arg1<<" | ";__f(comma+1, args...);
}

template<typename T, typename U> static inline void amin(T &x, U y) {
    if (y < x)
        x = y;
}

template<typename T, typename U> static inline void amax(T &x, U y) {
    if (x < y)
        x = y;
}

template<typename T,typename U> std::ostream& operator<<(std::ostream& out, std::pair<T,U> a) {
    out<<a.fi<<" "<<a.se;
    return out;
}

template<typename T,typename U> std::istream& operator>>(std::istream& in, std::pair<T,U> &a) {
    in>>a.fi>>a.se;
    return in;
}

inline long long toint(const std::string &s) {std::stringstream ss; ss << s; long long x; ss >> x; return x;}
inline std::string tostring(long long number) {std::stringstream ss; ss << number; return ss.str();}
inline std::string tobin(long long x) {return std::bitset<63>(x).to_string();}

std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());

#define ll long long
#define pii std::pair<int, int>
#define vi vector<int>
#define vvi vector<vi >
#define vii vector<pii >
#define point complex<ll>
#define ordered_set tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update>

/*----------------------Graph Moves----------------*/
//const int fx[]={+1,-1,+0,+0};
//const int fy[]={+0,+0,+1,-1};
//const int fx[]={+0,+0,+1,-1,-1,+1,-1,+1};   // Kings Move
//const int fy[]={-1,+1,+0,+0,+1,+1,-1,-1};  // Kings Move
//const int fx[]={-2, -2, -1, -1,  1,  1,  2,  2};  // Knights Move
//const int fy[]={-1,  1, -2,  2, -2,  2, -1,  1}; // Knights Move
/*------------------------------------------------*/

//primes for hashing 937,991,1013,1409,1741

using namespace std;
using namespace __gnu_pbds;
struct state{
    int len,link;
    int next[26];
};
const int maxn=100005;
struct SuffixAutomaton{
    state st[2*maxn];
    int size,last;
    ll ans;
    ll cnt[2*maxn],words[2*maxn];
    SuffixAutomaton(){
        st[0].len=0;
        st[0].link=-1;
        size=1;
        last=0;
        memset(words,0,sizeof(words));
        words[0]=1;
    }
    void extend(char c){
        int cur=size++;
        st[cur].len=st[last].len+1;
        int p=last;
        while(p!=-1&&!st[p].next[c]){
            st[p].next[c]=cur;
            words[cur]+=words[p];
            p=st[p].link;
        }
        if(p==-1){
            st[cur].link=0;
        }else{
            int q=st[p].next[c];
            if(st[p].len+1==st[q].len){
                st[cur].link=q;
            }else{
                int clone=size++;
                st[clone].len=st[p].len+1;
                for (int i = 0; i < 26; ++i) {
                    st[clone].next[i]=st[q].next[i];
                }
                st[clone].link=st[q].link;
                while(p!=-1&&st[p].next[c]==q){
                    st[p].next[c]=clone;
                    words[clone]+=words[p];
                    words[q]-=words[p];
                    p=st[p].link;
                }
                st[q].link=st[cur].link=clone;
            }
        }
        last=cur;
    }
    bool vis[2*maxn];
    bool term[2*maxn];
    void dfs(int v){
        if(vis[v])return ;
        vis[v]=1;
        cnt[v]=term[v];
        for (int i = 0; i < 26; ++i) {
            if(st[v].next[i]){
                dfs(st[v].next[i]);
                cnt[v]+=cnt[st[v].next[i]];
            }
        }
    }
    void build(string &s){
        for(auto &c:s){
            extend(c-'a');
        }
        memset(cnt,0,sizeof(cnt));
        memset(vis,0,sizeof(vis));
        memset(term,0,sizeof(term));
        int cur=last;
        term[cur]=1;
        while(cur){
            cur=st[cur].link;
            term[cur]=1;
        }
        dfs(0);
        for (int i = 1; i < size; ++i) {
            trace(i,cnt[i],words[i]);
            ans+=(cnt[i]*(cnt[i]+1)/2)*words[i];
        }
    }
};
void meowmeow321()
{
    string s;
    cin>>s;
    SuffixAutomaton sa;
    sa.build(s);
    cout<<sa.ans<<endl;
}
signed main()
{
    FAST;
    int testcases=1;
    //cin>>testcases;
    for(int i=0;i<testcases;++i)
    {
        meowmeow321();
    }
    cerr<<endl<<"Time Elasped : "<<elasped_time<<endl;
    return 0;
}


#include<stdio.h>
#include<iostream>
using namespace std;
const int maxn=200005,maxm=200005,maxk=30;
int i,j,k,m,n,e,tot,last;
int start[maxn],to[maxm],then[maxm],len[maxn],link[maxn],nxt[maxn][maxk],tag[maxn],size[maxn];
long long ans;
string s;
inline void add(int x,int y){
	then[++e]=start[x],start[x]=e,to[e]=y;
}
int extend(int last,int x){
	int pos=last,now=++tot,tmp,cln;
	tag[now]=1;
	len[now]=len[pos]+1;
	while(pos!=0&&nxt[pos][x]==0)
		nxt[pos][x]=now,pos=link[pos];
	if(pos==0){
		link[now]=1;
		return now;
	}
	tmp=nxt[pos][x];
	if(len[tmp]==len[pos]+1){
		link[now]=tmp;
		return now;
	}
	cln=++tot;
	len[cln]=len[pos]+1;
	for(int i=1;i<=26;i++)
		nxt[cln][i]=nxt[tmp][i];
	link[cln]=link[tmp],link[tmp]=link[now]=cln;
	while(pos!=0&&nxt[pos][x]==tmp)
		nxt[pos][x]=cln,pos=link[pos];
	return now;
}
void dfs(int x){
	size[x]=tag[x];
	for(int i=start[x];i;i=then[i]){
		int y=to[i];
		dfs(y);
		size[x]+=size[y];
	}
	if(x!=1)
		ans+=(long long)(len[x]-len[link[x]])*size[x]*(size[x]+1)/2;
}
int main(){
	cin>>s;
	last=tot=1;
	for(i=0;i<s.size();i++)
		last=extend(last,s[i]-96);
	for(i=2;i<=tot;i++)
		add(link[i],i);
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}


#include<stdio.h>
#include<iostream>
using namespace std;
const int maxn=200005,maxm=200005,maxk=30;
int i,j,k,m,n,e,tot,last;
int start[maxn],to[maxm],then[maxm],len[maxn],link[maxn],nxt[maxn][maxk],tag[maxn],size[maxn];
long long ans;
string s;
inline void add(int x,int y){
	then[++e]=start[x],start[x]=e,to[e]=y;
}
int extend(int last,int x){
	int pos=last,now=++tot,tmp,cln;
	tag[now]=1;
	len[now]=len[pos]+1;
	while(pos!=0&&nxt[pos][x]==0)
		nxt[pos][x]=now,pos=link[pos];
	if(pos==0){
		link[now]=1;
		return now;
	}
	tmp=nxt[pos][x];
	if(len[tmp]==len[pos]+1){
		link[now]=tmp;
		return now;
	}
	cln=++tot;
	len[cln]=len[pos]+1;
	for(int i=1;i<=26;i++)
		nxt[cln][i]=nxt[tmp][i];
	link[cln]=link[tmp],link[tmp]=link[now]=cln;
	while(pos!=0&&nxt[pos][x]==tmp)
		nxt[pos][x]=cln,pos=link[pos];
	return now;
}
void dfs(int x){
	size[x]=tag[x];
	for(int i=start[x];i;i=then[i]){
		int y=to[i];
		dfs(y);
		size[x]+=size[y];
	}
	if(x!=1)
		ans+=1ll*(len[x]-len[link[x]])*size[x]*(size[x]+1)/2ll;
}
int main(){
	cin>>s;
	last=tot=1;
	for(i=0;i<s.size();i++)
		last=extend(last,s[i]-96);
	for(i=2;i<=tot;i++)
		add(link[i],i);
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}


/* string coder = "Balajiganapathi"; */

//#define LOCAL
#ifdef LOCAL
#   define TRACE
#else
//#   define NDEBUG
#endif

#include<bits/stdc++.h>

using namespace std;

/* aliases */
using vi  = vector<int>;
using pi  = pair<int, int>;
using vs  = vector<string>;
using vpi = vector<pi>;
using ll  = long long int;

/* shortcut macros */
#define mp              make_pair
#define fi              first
#define se              second
#define mt              make_tuple
#define gt(t, i)        get<i>(t)
#define all(x)          (x).begin(), (x).end()
#define ini(a, v)       memset(a, v, sizeof(a))
#define re(i, s, n)     for(auto i = s, _##i = (n); i < _##i; ++i)
#define rep(i, s, n)    re(i, s, (n) + 1)
#define fo(i, n)        re(i, 0, n)
#define si(x)           (int)(x.size())
#define pu              push_back
#define is1(mask,i)     ((mask >> i) & 1)

/* trace macro */
#ifdef TRACE
#   define trace(v...)  {cerr << __func__ << ":" << __LINE__ << ": " ;_dt(#v, v);}
#else
#   define trace(...)
#endif

#ifdef TRACE
pi _gp(string s) {
    pi r(0, si(s) - 1);
    int p = 0, s1 = 0, s2 = 0, start = 1;
    fo(i, si(s)) {
        int x = (s1 | s2);
        if(s[i] == ' ' && start) {
            ++r.fi;
        } else {
            start = 0;
            if(s[i] == ',' && !p && !x) {
                r.se = i - 1;
                return r;
            }
            if(x && s[i] == '\\') ++i;
            else if(!x && s[i] == '(') ++p;
            else if(!x && s[i] == ')') --p;
            else if(!s2 && s[i] == '\'') s1 ^= 1;
            else if(!s1 && s[i] == '"') s2 ^= 1;
        }
    }
    return r;
}

template<typename H> void _dt(string u, H&& v) {
    pi p = _gp(u);
    cerr << u.substr(p.fi, p.se - p.fi + 1) << " = " << forward<H>(v) << " |" << endl;
}

template<typename H, typename ...T> void _dt(string u, H&& v, T&&... r) {
    pi p = _gp(u);
    cerr << u.substr(p.fi, p.se - p.fi + 1) << " = " << forward<H>(v) << " | ";
    _dt(u.substr(p.se + 2), forward<T>(r)...);
}

template<typename T>
ostream &operator <<(ostream &o, vector<T> v) { // print a vector
    o << "{";
    fo(i, si(v) - 1) o << v[i] << ", ";
    if(si(v)) o << v.back();
    o << "}";
    return o;
}

template<typename T1, typename T2>
ostream &operator <<(ostream &o, map<T1, T2> m) { // print a map
    o << "[";
    for(auto &p: m) {
        o << " (" << p.fi << " -> " << p.se << ")";
    }
    o << " ]";
    return o;
}

template <size_t n, typename... T>
typename enable_if<(n >= sizeof...(T))>::type
    print_tuple(ostream&, const tuple<T...>&) {}

template <size_t n, typename... T>
typename enable_if<(n < sizeof...(T))>::type
    print_tuple(ostream& os, const tuple<T...>& tup) {
    if (n != 0)
        os << ", ";
    os << get<n>(tup);
    print_tuple<n+1>(os, tup);
}

template <typename... T>
ostream& operator<<(ostream& os, const tuple<T...>& tup) { // print a tuple
    os << "(";
    print_tuple<0>(os, tup);
    return os << ")";
}

template <typename T1, typename T2>
ostream& operator<<(ostream& os, const pair<T1, T2>& p) { // print a pair
    return os << "(" << p.fi << ", " << p.se << ")";
}
#endif

/* util functions */

template<typename T>
string tostr(T x) {
    ostringstream oss;
    oss << x;
    return oss.str();
}

template<typename T1, typename T2, typename T3>
T1 modpow(T1 a, T2 p, T3 mod) {
    T1 ret = 1;

    a %= mod;
    for(; p > 0; p /= 2) {
        if(p & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
    }

    return ret;
}

#define x1 _asdfzx1
#define y1 _ysfdzy1

int dx[] {-1, 0, 1, 0, 1, 1, -1, -1};
int dy[] {0, -1, 0, 1, 1, -1, 1, -1};

/* constants */
constexpr auto PI  = 3.14159265358979323846L;
constexpr auto oo  = numeric_limits<int>::max() / 2 - 10;
constexpr auto eps = 1e-6;
constexpr auto mod = 1000000007;

/* code */
const int mx = 100005, log_mx = 19;
char str[mx];
int sa[mx], inv_sa[mx], sa_rank[mx][log_mx], lcp[mx];
int log_n, n;

class sa_entry {
    public:
    int rank[2], pos;

    bool operator <(const sa_entry &s) const {
        if(rank[0] == s.rank[0]) return rank[1] < s.rank[1];
        else return rank[0] < s.rank[0];
    }

    bool operator == (const sa_entry &s) const {
        return rank[0] == s.rank[0] && rank[1] == s.rank[1];
    }
} sa_nxt[mx];

int get_lcp(int x, int y) {
    int ret = 0;

    for(int i = log_n - 1; i >= 0 && x < n && y < n; --i) {
        if(sa_rank[x][i] == sa_rank[y][i]) {
            ret += (1 << i);
            x += (1 << i);
            y += (1 << i);
        }
    }

    return ret;
}

void build_sa() {
    for(int i = 0; i < n; ++i) sa_rank[i][0] = str[i];

    if(n == 1) sa_rank[0][0] = 0;

    int siz = 1;
    for(log_n = 1; siz < n; ++log_n, siz *= 2) {
        for(int i = 0; i < n; ++i) {
            sa_nxt[i].rank[0] = sa_rank[i][log_n - 1];
            sa_nxt[i].rank[1] = i + siz < n? sa_rank[i + siz][log_n - 1]: -1;
            sa_nxt[i].pos = i;
        }

        sort(sa_nxt, sa_nxt + n);
        for(int i = 0; i < n; ++i) {
            sa_rank[sa_nxt[i].pos][log_n] = (i > 0 && sa_nxt[i] == sa_nxt[i - 1])? sa_rank[sa_nxt[i - 1].pos][log_n]: i;
        }
    }

    for(int i = 0; i < n; ++i) {
        sa[sa_rank[i][log_n - 1]] = i;
    }

    for(int i = 0; i < n; ++i) inv_sa[sa[i]] = i;

    lcp[0] = 0;
    for(int i = 1; i < n; ++i) lcp[i] = get_lcp(sa[i - 1], sa[i]);
}

class SegNode {
    public:
    int cnt;
    ll sum;
    int cleared;
    SegNode() {
        cleared = cnt = sum = 0;
    }
    void clear() {
        cleared = 1;
        cnt = sum = 0;
    }
} seg[3 * mx];

ll ans = 0;

SegNode get(int i, int a, int b, int qa, int qb) {
    if(qa <= a && b <= qb) {
        //trace(i, a, b, qa, qb, seg[i].cnt, seg[i].sum);
        return seg[i];
    }
    int l = 2 * i + 1, r = 2 * i + 2;
    int m = (a + b) / 2;

    if(seg[i].cleared) {
        seg[i].cleared = 0;
        seg[l].clear();
        seg[r].clear();
    }
    if(qb <= m) return get(l, a, m, qa, qb);
    else if(qa > m) return get(r, m + 1, b, qa, qb);
    SegNode n1 = get(l, a, m, qa, qb);
    SegNode n2 = get(r, m + 1, b, qa, qb);
    SegNode ret;

    ret.sum = n1.sum + n2.sum;
    ret.cnt = n1.cnt + n2.cnt;

    return ret;
}

void clearIt(int i, int a, int b, int qa, int qb) {
    //trace(i, a, b, qa, qb);
    if(b < qa || a > qb) return;
    if(qa <= a && b <= qb) {
        seg[i].clear();
        return;
    }
    int l = 2 * i + 1, r = 2 * i + 2;
    int m = (a + b) / 2;

    if(seg[i].cleared) {
        seg[i].cleared = 0;
        seg[l].clear();
        seg[r].clear();
    }
    clearIt(l, a, m, qa, qb);
    clearIt(r, m + 1, b, qa, qb);

    seg[i].cnt = seg[l].cnt + seg[r].cnt;
    seg[i].sum = seg[l].sum + seg[r].sum;
    trace(i, seg[i].cnt, seg[i].sum);
}

void update(int i, int a, int b, int q, SegNode n) {
    if(q < a || q > b) return;
    if(a == b) {
        seg[i].cnt += n.cnt;
        seg[i].sum += n.sum;
        return;
    }
    int l = 2 * i + 1, r = 2 * i + 2;
    int m = (a + b) / 2;

    if(seg[i].cleared) {
        seg[i].cleared = 0;
        seg[l].clear();
        seg[r].clear();
    }

    if(q <= m) update(l, a, m, q, n);
    else update(r, m + 1, b, q, n);

    seg[i].cnt = seg[l].cnt + seg[r].cnt;
    seg[i].sum = seg[l].sum + seg[r].sum;
    //trace(i, a, b, q, seg[i].cnt, seg[i].sum);
}

void proc(int i) {
    int x = lcp[i];
    ll cur = x;
    SegNode larger = get(0, 0, n, x + 1, n);
    trace(x, larger.cnt, larger.sum);
    cur += 1ll * larger.cnt * x;
    SegNode notLarger = get(0, 0, n, 0, x);
    trace(x, notLarger.cnt, notLarger.sum);

    cur += notLarger.sum;
    /*
    ll tmp = x;
    int tc = x;
    for(int j = i - 1; j > 0; --j) {
        tc = min(tc, lcp[j]);
        tmp += tc;
    }

    if(cur != tmp) {
        trace(i, x, cur, tmp);
        assert(cur == tmp);
    }
    */

    clearIt(0, 0, n, x + 1, n);
    SegNode updateNode;
    updateNode.cnt = larger.cnt + 1;
    updateNode.sum = 1ll * updateNode.cnt * x;
    update(0, 0, n, x, updateNode);

    ans += cur;
    trace(x, cur, ans, updateNode.cnt, updateNode.sum);

}

int main() {
    scanf("%s", str);
    n = strlen(str);
    build_sa();

    ans += 1ll * n * (n + 1) / 2;

    re(i, 1, n) {
        trace(i, lcp[i]);
        proc(i);
    }

    cout << ans << endl;

	return 0;
}