#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vd = vector<double>;
using vs = vector<string>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pdd = pair<double, double>;
using vpii = vector<pii>;
using vpll = vector<pll>;
using vpdd = vector<pdd>;
template <typename T>
void ckmin(T& a, const T& b) {
  a = min(a, b);
}
template <typename T>
void ckmax(T& a, const T& b) {
  a = max(a, b);
}
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
namespace __input {
template <class T1, class T2>
void re(pair<T1, T2>& p);
template <class T>
void re(vector<T>& a);
template <class T, size_t SZ>
void re(array<T, SZ>& a);
template <class T>
void re(T& x) {
  cin >> x;
}
void re(double& x) {
  string t;
  re(t);
  x = stod(t);
}
template <class Arg, class... Args>
void re(Arg& first, Args&... rest) {
  re(first);
  re(rest...);
}
template <class T1, class T2>
void re(pair<T1, T2>& p) {
  re(p.first, p.second);
}
template <class T>
void re(vector<T>& a) {
  for (int i = 0; i < (int((a).size())); i++) re(a[i]);
}
template <class T, size_t SZ>
void re(array<T, SZ>& a) {
  for (int i = 0; i < (SZ); i++) re(a[i]);
}
}  // namespace __input
using namespace __input;
namespace __output {
template <class T1, class T2>
void pr(const pair<T1, T2>& x);
template <class T, size_t SZ>
void pr(const array<T, SZ>& x);
template <class T>
void pr(const vector<T>& x);
template <class T>
void pr(const set<T>& x);
template <class T1, class T2>
void pr(const map<T1, T2>& x);
template <class T>
void pr(const T& x) {
  cout << x;
}
template <class Arg, class... Args>
void pr(const Arg& first, const Args&... rest) {
  pr(first);
  pr(rest...);
}
template <class T1, class T2>
void pr(const pair<T1, T2>& x) {
  pr("{", x.first, ", ", x.second, "}");
}
template <class T, bool pretty = true>
void prContain(const T& x) {
  if (pretty) pr("{");
  bool fst = 1;
  for (const auto& a : x) pr(!fst ? pretty ? ", " : " " : "", a), fst = 0;
  if (pretty) pr("}");
}
template <class T>
void pc(const T& x) {
  prContain<T, false>(x);
  pr("\n");
}
template <class T, size_t SZ>
void pr(const array<T, SZ>& x) {
  prContain(x);
}
template <class T>
void pr(const vector<T>& x) {
  prContain(x);
}
template <class T>
void pr(const set<T>& x) {
  prContain(x);
}
template <class T1, class T2>
void pr(const map<T1, T2>& x) {
  prContain(x);
}
void ps() { pr("\n"); }
template <class Arg>
void ps(const Arg& first) {
  pr(first);
  ps();
}
template <class Arg, class... Args>
void ps(const Arg& first, const Args&... rest) {
  pr(first, " ");
  ps(rest...);
}
}  // namespace __output
using namespace __output;
namespace __algorithm {
template <typename T>
void dedup(vector<T>& v) {
  sort((v).begin(), (v).end());
  v.erase(unique((v).begin(), (v).end()), v.end());
}
template <typename T>
typename vector<T>::iterator find(const vector<T>& v, const T& x) {
  auto it = lower_bound((v).begin(), (v).end(), x);
  return it != v.end() && *it == x ? it : v.end();
}
template <typename T>
size_t index(const vector<T>& v, const T& x) {
  auto it = find(v, x);
  assert(it != v.end() && *it == x);
  return it - v.begin();
}
template <typename T1, typename T2>
typename vector<pair<T1, T2>>::iterator lower_bound(
    const vector<pair<T1, T2>>& v, const T1& x) {
  return lower_bound(
      (v).begin(), (v).end(), x,
      [](pair<T1, T2> a, pair<T1, T2> b) { return a.first < b.first; });
}
template <typename T1, typename T2>
typename vector<pair<T1, T2>>::iterator upper_bound(
    const vector<pair<T1, T2>>& v, const T1& x) {
  return upper_bound(
      (v).begin(), (v).end(), x,
      [](pair<T1, T2> a, pair<T1, T2> b) { return a.first < b.first; });
}
}  // namespace __algorithm
using namespace __algorithm;
struct monostate {
  friend istream& operator>>(istream& is,
                             const __attribute__((unused)) monostate& ms) {
    return is;
  }
  friend ostream& operator<<(ostream& os,
                             const __attribute__((unused)) monostate& ms) {
    return os;
  }
} ms;
template <typename W = monostate>
struct wedge {
  int u, v, i;
  W w;
  wedge<W>(int _u = -1, int _v = -1, int _i = -1) : u(_u), v(_v), i(_i) {}
  int operator[](int loc) const { return u ^ v ^ loc; }
  friend void re(wedge& e) {
    re(e.u, e.v, e.w);
    --e.u, --e.v;
  }
  friend void pr(const wedge& e) { pr(e.u, "<-", e.w, "->", e.v); }
};
namespace __io {
void setIn(string second) { freopen(second.c_str(), "r", stdin); }
void setOut(string second) { freopen(second.c_str(), "w", stdout); }
void setIO(string second = "") {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  if (int((second).size())) {
    setIn(second + ".in"), setOut(second + ".out");
  }
}
}  // namespace __io
using namespace __io;
template <typename E>
struct tree {
  int N;
  vvi adj;
  vector<E> edges;
  tree() {}
  tree(int _N) : N(_N) {
    adj.resize(N);
    edges.resize(N - 1);
  }
  tree(const vector<E>& __edges) {
    adj.resize(int((edges).size()) + 1);
    edges = __edges;
    __init();
  }
  void __init() {
    for (int i = 0; i < (int((edges).size())); i++) {
      auto& e = edges[i];
      assert(0 <= e.u && e.u < N), adj[e.u].push_back(i);
      assert(0 <= e.v && e.v < N), adj[e.v].push_back(i);
      e.i = i;
    }
  }
  friend void re(tree& t) {
    re(t.edges);
    t.__init();
  }
  friend void pr(const tree& t) { pr("{N=", t.N, " ", t.edges, "}"); }
  template <typename T>
  void go(vector<T>& down, int loc, int ine) const {
    T& res = down[loc] = T::leaf(loc);
    for (auto& ei : adj[loc])
      if (ei != ine) {
        int cloc = edges[ei][loc];
        res.pre_process(down[cloc]);
        go(down, cloc, ei);
        res.attach(down[cloc].extend());
      }
    res.post_process();
  }
  template <typename T>
  vector<T> down(int root) const {
    vector<T> down(N);
    go(down, root, -1);
    return down;
  }
  template <typename T>
  void again(vector<T>& down, vector<T>& up, int loc, int ine) const {
    const int D = int((adj[loc]).size());
    vector<T> pref(D), suff(D);
    for (int i = 0; i < (D); i++) {
      const E& e = edges[adj[loc][i]];
      pref[i] = suff[i] = ((e.i != ine) ? down[e[loc]] : up[loc]).extend();
    }
    for (int i = 0; i < (D - 1); i++) pref[i + 1].accumulate(pref[i]);
    for (int i = (D - 1) - 1; i >= 0; i--) suff[i].accumulate(suff[i + 1]);
    for (int i = 0; i < (D); i++) {
      const E& e = edges[adj[loc][i]];
      if (e.i != ine) {
        int cloc = e[loc];
        T& cres = up[cloc] = T::leaf(loc);
        if (i) cres.attach(pref[i - 1]);
        if (i + 1 < D) cres.attach(suff[i + 1]);
        cres.post_process();
        again(down, up, e[loc], e.i);
      }
    }
  }
  template <typename T>
  array<vector<T>, 3> both() const {
    vector<T> down(N);
    go(down, 0, -1);
    vector<T> up(N);
    again(down, up, 0, -1);
    vector<T> root = down;
    for (int i = (1); i < (N); i++) {
      root[i].attach(up[i].extend());
      root[i].post_process();
    }
    return {down, up, root};
  }
};
using edge = wedge<>;
ll nc2(ll v) { return v * (v - 1) / 2; }
int N;
struct subtree {
  int id, size, depth;
  ll ans, rpath;
  vector<pair<int, ll>> cchoice;
  static subtree leaf(int id) { return {id, 1, 0, LLONG_MAX, 0ll, {}}; }
  void pre_process(subtree& child) { child.depth = depth + 1; }
  const subtree& extend() { return *this; }
  void attach(const subtree& o) {
    size += o.size;
    cchoice.emplace_back(o.size, o.rpath);
    ckmin(ans, o.ans);
  }
  const subtree& post_process() {
    decltype(cchoice) shrunk;
    sort((cchoice).begin(), (cchoice).end());
    for (auto& elt : cchoice)
      if (int((shrunk).size()) < 2 ||
          shrunk[int((shrunk).size()) - 2].first != elt.first)
        shrunk.push_back(elt);
    rpath = nc2(size);
    for (auto [ss, rc] : shrunk) ckmin(rpath, rc + nc2(size - ss));
    for (auto it1 = shrunk.begin(); it1 != shrunk.end(); it1++) {
      ckmin(ans, it1->second + nc2(N - it1->first));
      for (auto it2 = next(it1); it2 != shrunk.end(); it2++) {
        ckmin(ans,
              it1->second + it2->second + nc2(N - it1->first - it2->first));
      }
    }
    return *this;
  }
  void accumulate(const subtree& o) { assert(false); }
  friend void pr(const subtree& second) {
    pr("{id=", second.id, " sz=", second.size, "}");
  }
};
int main() {
  setIO();
  re(N);
  tree<edge> tr(N);
  re(tr);
  ps(2 * nc2(N) - tr.down<subtree>(0)[0].ans);
  return 0;
}
