Problem:
G - リングと紐
Problem Statement
芸術家のみさわさんは新しい作品を作ろうとしている．
新しい作品の材料として，みさわさんは金属の輪と白黒2色の紐を大量に用意した．
みさわさんは，異なる 2 つの輪をいずれかの色の紐で結ぶことを繰り返すことによって作品を作ることが出来る．
1 つの輪を複数の輪と繋ぐことも可能であるが，それぞれの輪のペアを結ぶ紐は高々 1 本である．
みさわさんは，せっかく作品を作るからにはよい作品を作りたいと考えた．
みさわさんの考える「よい作品」とは，以下のように定義されるものである．
1 つの輪は 1 つのよい作品である．
2 つのよい作品を用意したとき，それらに含まれている紐で繋がれていない輪のペア全てを白い紐で繋いだものは， 1 つのよい作品である．(図1. 操作1)
よい作品の白い紐を黒い紐に，黒い紐を白い紐にすべて入れ替えたものはよい作品である．(図1. 操作2)
以上で定義されたもののみがよい作品である．
図 1 はよい作品の一例である．
図 1. よい作品の例
以上の条件を満たすような渾身の「よい作品」を作り上げたみさわさんは，この作品に含まれる 0 個以上の輪を選び，それらを天井から吊るして展示することにした．
このとき，なるべく黒い紐が映えるように展示したい．
すなわち，天井から吊るされている輪とそれ以外の輪の間を繋ぐ黒い紐の本数が最大になるようにしたい．
そのように展示した時の，天井から吊るされている輪とそれ以外の輪の間を繋ぐ黒い紐の本数を求めよ．
Input
入力は以下の形式で与えられる．
$N$ $M$
$u_1$ $v_1$
...
$u_M$ $v_M$
$N$ $(1 \leq N \leq 5000)$ は作品に含まれる輪の数である．
$M$ $(1 \leq M \leq 100000)$ は作品に含まれる黒い紐の本数である．
$u_i$, $v_i$ ($1 \leq u_i, v_i \leq N$)は $i$ 番目の黒い紐が $u_i$ 番目の輪と $v_i$ 番目の輪を繋いでいることを表す．
入力で指定されなかった輪のペアに関しては白い紐で繋がれている．
入力で与えられる作品は「よい作品」であることが保証されている．
Output
天井から吊るされている輪とそれ以外の輪の間を繋ぐ黒い紐の本数の最大値を求めよ．
Sample Input 1
5 4
1 2
2 3
3 1
4 5
Output for the Sample Input 1
3
Sample Input 2
6 5
1 2
1 3
1 4
1 5
3 4
Output for the Sample Input 2
4
Sample Input 3
5 8
2 5
5 4
1 3
1 5
4 1
2 4
3 4
3 2
Output for the Sample Input 3
6

Submitted solutions:

code_snippet: #line 1 "library/Template/template.hpp"
#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}
#line 2 "library/Utility/fastio.hpp"
#include <unistd.h>
namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf

uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio

using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */
#line 3 "sol.cpp"

#line 2 "library/DataStructure/unionfind.hpp"

struct UnionFind{
    vector<int> par; int n;
    UnionFind(){}
    UnionFind(int _n):par(_n,-1),n(_n){}
    int root(int x){return par[x]<0?x:par[x]=root(par[x]);}
    bool same(int x,int y){return root(x)==root(y);}
    int size(int x){return -par[root(x)];}
    bool unite(int x,int y){
        x=root(x),y=root(y); if(x==y)return false;
        if(size(x)>size(y))swap(x,y);
        par[y]+=par[x]; par[x]=y; n--; return true;
    }
};

/**
 * @brief Union Find
 */
#line 5 "sol.cpp"

int main() {
    int n, m;
    read(n, m);
    vector g(n, vector<int>(n));
    rep(_, 0, m) {
        int x, y;
        read(x, y);
        x--, y--;
        g[x][y] = g[y][x] = 1;
    }

    auto rec = [&](auto &rec, vector<int> &vs) -> vector<int> {
        if (SZ(vs) <= 1)
            return vector<int>(SZ(vs) + 1);
        int sz = SZ(vs);
        UnionFind uni(sz);
        rep(i, 0, sz) rep(j, 0, sz) if (g[vs[i]][vs[j]]) {
            uni.unite(i, j);
        }
        if (uni.n == 1) {
            UnionFind uni2(sz);
            rep(i, 0, sz) rep(j, 0, sz) if (!g[vs[i]][vs[j]]) {
                uni2.unite(i, j);
            }
            map<int, vector<int>> grp;
            rep(i, 0, sz) grp[uni2.root(i)].push_back(vs[i]);
            vector<int> ret(1);
            for (auto &[_, nv] : grp) {
                auto add = rec(rec, nv);
                vector<int> nxt(SZ(ret) + SZ(add) - 1);
                int A = SZ(ret) - 1, B = SZ(add) - 1;
                rep(i, 0, SZ(ret)) rep(j, 0, SZ(add)) chmax(
                    nxt[i + j], ret[i] + add[j] + (i + j) * ((A + B) - i - j) -
                                    i * (A - i) - j * (B - j));
                swap(ret, nxt);
            }
            return ret;
        } else {
            map<int, vector<int>> grp;
            rep(i, 0, sz) grp[uni.root(i)].push_back(vs[i]);
            vector<int> ret(1);
            for (auto &[_, nv] : grp) {
                auto add = rec(rec, nv);
                vector<int> nxt(SZ(ret) + SZ(add) - 1);
                rep(i, 0, SZ(ret)) rep(j, 0, SZ(add))
                    chmax(nxt[i + j], ret[i] + add[j]);
                swap(ret, nxt);
            }
            return ret;
        }
    };

    vector<int> init(n);
    iota(ALL(init), 0);
    auto dp = rec(rec, init);
    print(MAX(dp));
    return 0;
}
accuracy: 1
time_ms: 600
memory_kb: 104528
final_rank: 9

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

static bool G[5000][5000] = {};

vector<int> DFS(vector<int>& V) {
    if (V.size() == 0) return {0};
    if (V.size() == 1) return {0,0};
    int N = V.size();
    vector<int> A = {0};
    dsu UF(N);
    rep(i,0,N) rep(j,0,N) if (G[V[i]][V[j]]) UF.merge(i,j);
    if (UF.size(0) != N) {
        vector<vector<int>> NV(N);
        rep(i,0,N) NV[UF.leader(i)].push_back(V[i]);
        rep(i,0,N) {
            if (NV[i].empty()) continue;
            vector<int> B = DFS(NV[i]);
            vector<int> C(A.size() + B.size() - 1,0);
            rep(j,0,A.size()) rep(k,0,B.size()) chmax(C[j+k],A[j]+B[k]);
            A = C;
        }
        return A;
    }
    else {
        dsu UF2(N);
        rep(i,0,N) rep(j,0,N) if (!G[V[i]][V[j]]) UF2.merge(i,j);
        vector<vector<int>> NV(N);
        rep(i,0,N) NV[UF2.leader(i)].push_back(V[i]);
        rep(i,0,N) {
            if (NV[i].empty()) continue;
            vector<int> B = DFS(NV[i]);
            vector<int> C(A.size() + B.size() - 1,0);
            rep(j,0,A.size()) rep(k,0,B.size()) chmax(C[j+k],A[j]+B[k]+j*((int)B.size()-1-k)+k*((int)A.size()-1-j));
            A = C;
        }
        return A;
    }
}

int main() {
    int N, M;
    cin >> N >> M;
    rep(i,0,M) {
        int X, Y;
        cin >> X >> Y;
        X--, Y--;
        G[X][Y] = G[Y][X] = true;
    }
    vector<int> V(N);
    iota(ALL(V),0);
    vector<int> ANS = DFS(V);
    int MAX = 0;
    rep(i,0,N+1) chmax(MAX, ANS[i]);
    cout << MAX << endl;
}
accuracy: 1
time_ms: 560
memory_kb: 34624
final_rank: 2

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b) for (int i = (int)(a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b)-1; i >= (int)(a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())

using uint = unsigned int;
using ll = long long int;
using ull = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
const int inf = 0x3fffffff;
const ll INF = 0x1fffffffffffffff;

template <typename T> inline bool chmax(T &a, T b) {
    if (a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T> inline bool chmin(T &a, T b) {
    if (a > b) {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T, typename U> T ceil(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? (x + y - 1) / y : x / y);
}
template <typename T, typename U> T floor(T x, U y) {
    assert(y != 0);
    if (y < 0)
        x = -x, y = -y;
    return (x > 0 ? x / y : (x - y + 1) / y);
}
template <typename T> int popcnt(T x) {
    return __builtin_popcountll(x);
}
template <typename T> int topbit(T x) {
    return (x == 0 ? -1 : 63 - __builtin_clzll(x));
}
template <typename T> int lowbit(T x) {
    return (x == 0 ? -1 : __builtin_ctzll(x));
}

template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << "P(" << p.first << ", " << p.second << ")";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) {
    os << "{";
    for (int i = 0; i < vec.size(); i++) {
        os << vec[i] << (i + 1 == vec.size() ? "" : ", ");
    }
    os << "}";
    return os;
}
template <typename T, typename U>
ostream &operator<<(ostream &os, const map<T, U> &map_var) {
    os << "{";
    for (auto itr = map_var.begin(); itr != map_var.end(); itr++) {
        os << "(" << itr->first << ", " << itr->second << ")";
        itr++;
        if (itr != map_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
template <typename T> ostream &operator<<(ostream &os, const set<T> &set_var) {
    os << "{";
    for (auto itr = set_var.begin(); itr != set_var.end(); itr++) {
        os << *itr;
        ++itr;
        if (itr != set_var.end())
            os << ", ";
        itr--;
    }
    os << "}";
    return os;
}
#ifdef LOCAL
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define show(...) true
#endif
template <typename T> void _show(int i, T name) {
    cerr << '\n';
}
template <typename T1, typename T2, typename... T3>
void _show(int i, const T1 &a, const T2 &b, const T3 &...c) {
    for (; a[i] != ',' && a[i] != '\0'; i++)
        cerr << a[i];
    cerr << ":" << b << " ";
    _show(i + 1, a, c...);
}

#include <unistd.h>

namespace fastio {
static constexpr uint32_t SZ = 1 << 17;
char ibuf[SZ];
char obuf[SZ];
char out[100];
// pointer of ibuf, obuf


uint32_t pil = 0, pir = 0, por = 0;

struct Pre {
    char num[10000][4];
    constexpr Pre() : num() {
        for (int i = 0; i < 10000; i++) {
            int n = i;
            for (int j = 3; j >= 0; j--) {
                num[i][j] = n % 10 | '0';
                n /= 10;
            }
        }
    }
} constexpr pre;

inline void load() {
    memmove(ibuf, ibuf + pil, pir - pil);
    pir = pir - pil + fread(ibuf + pir - pil, 1, SZ - pir + pil, stdin);
    pil = 0;
    if (pir < SZ)
        ibuf[pir++] = '\n';
}

inline void flush() {
    fwrite(obuf, 1, por, stdout);
    por = 0;
}

void rd(char &c) {
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
}

void rd(string &x) {
    x.clear();
    char c;
    do {
        if (pil + 1 > pir)
            load();
        c = ibuf[pil++];
    } while (isspace(c));
    do {
        x += c;
        if (pil == pir)
            load();
        c = ibuf[pil++];
    } while (!isspace(c));
}

template <typename T> void rd_real(T &x) {
    string s;
    rd(s);
    x = stod(s);
}

template <typename T> void rd_integer(T &x) {
    if (pil + 100 > pir)
        load();
    char c;
    do
        c = ibuf[pil++];
    while (c < '-');
    bool minus = 0;
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (c == '-') {
            minus = 1, c = ibuf[pil++];
        }
    }
    x = 0;
    while ('0' <= c) {
        x = x * 10 + (c & 15), c = ibuf[pil++];
    }
    if constexpr (is_signed<T>::value || is_same_v<T, i128>) {
        if (minus)
            x = -x;
    }
}

void rd(int &x) {
    rd_integer(x);
}
void rd(ll &x) {
    rd_integer(x);
}
void rd(i128 &x) {
    rd_integer(x);
}
void rd(uint &x) {
    rd_integer(x);
}
void rd(ull &x) {
    rd_integer(x);
}
void rd(u128 &x) {
    rd_integer(x);
}
void rd(double &x) {
    rd_real(x);
}
void rd(long double &x) {
    rd_real(x);
}

template <class T, class U> void rd(pair<T, U> &p) {
    return rd(p.first), rd(p.second);
}
template <size_t N = 0, typename T> void rd_tuple(T &t) {
    if constexpr (N < std::tuple_size<T>::value) {
        auto &x = std::get<N>(t);
        rd(x);
        rd_tuple<N + 1>(t);
    }
}
template <class... T> void rd(tuple<T...> &tpl) {
    rd_tuple(tpl);
}

template <size_t N = 0, typename T> void rd(array<T, N> &x) {
    for (auto &d : x)
        rd(d);
}
template <class T> void rd(vector<T> &x) {
    for (auto &d : x)
        rd(d);
}

void read() {}
template <class H, class... T> void read(H &h, T &...t) {
    rd(h), read(t...);
}

void wt(const char c) {
    if (por == SZ)
        flush();
    obuf[por++] = c;
}
void wt(const string s) {
    for (char c : s)
        wt(c);
}
void wt(const char *s) {
    size_t len = strlen(s);
    for (size_t i = 0; i < len; i++)
        wt(s[i]);
}

template <typename T> void wt_integer(T x) {
    if (por > SZ - 100)
        flush();
    if (x < 0) {
        obuf[por++] = '-', x = -x;
    }
    int outi;
    for (outi = 96; x >= 10000; outi -= 4) {
        memcpy(out + outi, pre.num[x % 10000], 4);
        x /= 10000;
    }
    if (x >= 1000) {
        memcpy(obuf + por, pre.num[x], 4);
        por += 4;
    } else if (x >= 100) {
        memcpy(obuf + por, pre.num[x] + 1, 3);
        por += 3;
    } else if (x >= 10) {
        int q = (x * 103) >> 10;
        obuf[por] = q | '0';
        obuf[por + 1] = (x - q * 10) | '0';
        por += 2;
    } else
        obuf[por++] = x | '0';
    memcpy(obuf + por, out + outi + 4, 96 - outi);
    por += 96 - outi;
}

template <typename T> void wt_real(T x) {
    ostringstream oss;
    oss << fixed << setprecision(15) << double(x);
    string s = oss.str();
    wt(s);
}

void wt(int x) {
    wt_integer(x);
}
void wt(ll x) {
    wt_integer(x);
}
void wt(i128 x) {
    wt_integer(x);
}
void wt(uint x) {
    wt_integer(x);
}
void wt(ull x) {
    wt_integer(x);
}
void wt(u128 x) {
    wt_integer(x);
}
void wt(double x) {
    wt_real(x);
}
void wt(long double x) {
    wt_real(x);
}

template <class T, class U> void wt(const pair<T, U> val) {
    wt(val.first);
    wt(' ');
    wt(val.second);
}
template <size_t N = 0, typename T> void wt_tuple(const T t) {
    if constexpr (N < std::tuple_size<T>::value) {
        if constexpr (N > 0) {
            wt(' ');
        }
        const auto x = std::get<N>(t);
        wt(x);
        wt_tuple<N + 1>(t);
    }
}
template <class... T> void wt(tuple<T...> tpl) {
    wt_tuple(tpl);
}
template <class T, size_t S> void wt(const array<T, S> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}
template <class T> void wt(const vector<T> val) {
    auto n = val.size();
    for (size_t i = 0; i < n; i++) {
        if (i)
            wt(' ');
        wt(val[i]);
    }
}

void print() {
    wt('\n');
}
template <class Head, class... Tail> void print(Head &&head, Tail &&...tail) {
    wt(head);
    if (sizeof...(Tail))
        wt(' ');
    print(forward<Tail>(tail)...);
}
void __attribute__((destructor)) _d() {
    flush();
}
} // namespace fastio


using fastio::flush;
using fastio::print;
using fastio::read;

inline void first(bool i = true) {
    print(i ? "first" : "second");
}
inline void Alice(bool i = true) {
    print(i ? "Alice" : "Bob");
}
inline void Takahashi(bool i = true) {
    print(i ? "Takahashi" : "Aoki");
}
inline void yes(bool i = true) {
    print(i ? "yes" : "no");
}
inline void Yes(bool i = true) {
    print(i ? "Yes" : "No");
}
inline void No() {
    print("No");
}
inline void YES(bool i = true) {
    print(i ? "YES" : "NO");
}
inline void NO() {
    print("NO");
}
inline void Yay(bool i = true) {
    print(i ? "Yay!" : ":(");
}
inline void Possible(bool i = true) {
    print(i ? "Possible" : "Impossible");
}
inline void POSSIBLE(bool i = true) {
    print(i ? "POSSIBLE" : "IMPOSSIBLE");
}

/**
 * @brief Fast IO
 */

struct dsu {
  public:
    dsu() : _n(0) {}
    dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    // root node: -1 * component size
    // otherwise: parent
    std::vector<int> parent_or_size;
};

static bool G[5000][5000] = {};

vector<int> DFS(vector<int>& V) {
    if (V.size() == 0) return {0};
    if (V.size() == 1) return {0,0};
    int N = V.size();
    vector<int> A = {0};
    dsu UF(N);
    rep(i,0,N) rep(j,0,N) if (G[V[i]][V[j]]) UF.merge(i,j);
    if (UF.size(0) != N) {
        vector<vector<int>> NV(N);
        rep(i,0,N) NV[UF.leader(i)].push_back(V[i]);
        rep(i,0,N) {
            if (NV[i].empty()) continue;
            vector<int> B = DFS(NV[i]);
            vector<int> C(A.size() + B.size() - 1,0);
            rep(j,0,A.size()) rep(k,0,B.size()) chmax(C[j+k],A[j]+B[k]);
            swap(A,C);
        }
        return A;
    }
    else {
        dsu UF2(N);
        rep(i,0,N) rep(j,0,N) if (!G[V[i]][V[j]]) UF2.merge(i,j);
        vector<vector<int>> NV(N);
        rep(i,0,N) NV[UF2.leader(i)].push_back(V[i]);
        rep(i,0,N) {
            if (NV[i].empty()) continue;
            vector<int> B = DFS(NV[i]);
            vector<int> C(A.size() + B.size() - 1,0);
            rep(j,0,A.size()) rep(k,0,B.size()) chmax(C[j+k],A[j]+B[k]+j*((int)B.size()-1-k)+k*((int)A.size()-1-j));
            swap(A,C);
        }
        return A;
    }
}

int main() {
    int N, M;
    cin >> N >> M;
    rep(i,0,M) {
        int X, Y;
        cin >> X >> Y;
        X--, Y--;
        G[X][Y] = G[Y][X] = true;
    }
    vector<int> V(N);
    iota(ALL(V),0);
    vector<int> ANS = DFS(V);
    int MAX = 0;
    rep(i,0,N+1) chmax(MAX, ANS[i]);
    cout << MAX << endl;
}
accuracy: 1
time_ms: 580
memory_kb: 34552
final_rank: 4

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

struct UnionFind
{
  int n;
  vector<int> par;
  
  UnionFind() = default;
  UnionFind(int _n) : n(_n), par(_n)
  {
    for (int i = 0; i < n; ++i) par[i] = i;
  }
  
  int root(int x)
  {
    if (par[x] == x) return x;
    else return par[x] = root(par[x]);    
  }
  
  void merge(int x, int y)
  {
    int rx = root(x), ry = root(y);
    par[ry] = rx;
    return;
  }
  
  vector<vector<int>> group()
  {
    int siz = 0;
    vector<int> seen(n, -1);
    vector<vector<int>> ret(n);
    for (int i = 0; i < n; ++i)
    {
      int ri = root(i);
      if (seen[ri] == -1) seen[ri] = siz++;
      ret[seen[ri]].emplace_back(i);
    }
    ret.resize(siz);
    return ret;
  }
};

template<class T>
bool chmax(T &a, const T &b) {if (a < b) {a = b; return 1;} return 0;}

vector<int> dfs(bool isblack, vector<int> &ids, const vector<vector<bool>> &color)
{
  int N = color.size(), siz = ids.size();
  
  if (siz <= 1)
  {
    vector<int> ret(siz + 1, 0);
    return ret;
  }
  
  UnionFind uf(siz);
  for (int i = 0; i < siz; ++i)
  {
    int idi = ids[i];
    for (int j = 0; j < siz; ++j)
    {
      int idj = ids[j];
      if (i == j) continue;
      if (color[idi][idj] != isblack) uf.merge(i, j);
    }
  }
  
  vector<vector<int>> group = uf.group();
  // cout << -1 << " " << isblack << endl;
  // for (auto id : ids) cout << id << " ";
  // cout << endl;
  // for (auto g : group)
  // {
  //   for (auto v : g) cout << v << " ";
  //   cout << endl;
  // }
  
  assert(group.size() > 1);
  
  vector<int> ret{0};
  for (auto g : group)
  {
    vector<int> gid;
    for (auto v : g) gid.emplace_back(ids[v]);
    vector<int> vec = dfs(!isblack, gid, color);
    int s0 = (int)ret.size() - 1;
    int s1 = (int)vec.size() - 1;
    // cout << s0 << " " << s1 << " " << g.size() << endl;
    
    vector<int> nret(s0 + s1 + 1, 0);
    for (int c0 = 0; c0 <= s0; ++c0)
    {
      for (int c1 = 0; c1 <= s1; ++c1)
      {
        int tmp = ret[c0] + vec[c1];
        if (isblack) tmp += c0 * (s1 - c1) + c1 * (s0 - c0);
        chmax(nret[c0 + c1], tmp);
      }
    }
    
    swap(ret, nret);
  }
  
  assert((int)ret.size() == siz + 1);
  
  return ret;
}

void solve()
{
  int N, M; cin >> N >> M;
  vector<vector<bool>> color(N, vector<bool>(N, false));
  UnionFind uf(N);
  for (int i = 0; i < M; ++i)
  {
    int u, v; cin >> u >> v;
    u--, v--;
    color[u][v] = color[v][u] = true;
    uf.merge(u, v);
  }
  
  vector<int> al(N);
  iota(al.begin(), al.end(), 0);
  
  vector<int> ret;
  if (uf.group().size() == 1) ret = dfs(true, al, color);
  else ret = dfs(false, al, color);
  
  int res = *max_element(ret.begin(), ret.end());
  cout << res << endl;
  
  return;
}

int main()
{
  solve();
  
  return 0;
}
accuracy: 1
time_ms: 490
memory_kb: 14968
final_rank: 1

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

#define ll long long
#define elif else if
#define vi vector<int>
#define vll vector<ll>
#define vvi vector<vi>
#define pii pair<int,int>


#define repname(a, b, c, d, e, ...) e
#define rep(...)                    repname(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define rep0(x)                     for (int rep_counter = 0; rep_counter < (x); ++rep_counter)
#define rep1(i, x)                  for (int i = 0; i < (x); ++i)
#define rep2(i, l, r)               for (int i = (l); i < (r); ++i)
#define rep3(i, l, r, c)            for (int i = (l); i < (r); i += (c))





struct ScalarInput {
    template<class T>
    operator T(){
        T ret;
        cin >> ret;
        return ret;
    }
};
struct VectorInput {
    size_t n;
    VectorInput(size_t n): n(n) {}
    template<class T>
    operator vector<T>(){
        vector<T> ret(n);
        for(T &x : ret) cin >> x;
        return ret;
    }
};
ScalarInput input(){ return ScalarInput(); }
VectorInput input(size_t n){ return VectorInput(n); }

template<typename T>
void print(vector<T> a){
  for(int i=0;i<a.size();i++){
    cout<<a[i]<<" \n"[i+1==a.size()];
  }
}

template<class T>
void print(T x){
    cout << x << '\n';
}
 
template <class Head, class... Tail>
void print(Head&& head, Tail&&... tail){
  cout << head << ' ';
  print(forward<Tail>(tail)...);
}

double stop_watch(struct timespec start_time) {
    // 経過時間を秒単位で返す.
    struct timespec end_time;
    clock_gettime(CLOCK_REALTIME, &end_time);
    long long int sec = end_time.tv_sec - start_time.tv_sec;
    long long int nsec = end_time.tv_nsec - start_time.tv_nsec;
    return (double)sec + (double)nsec / (1000 * 1000 * 1000);
}

double randdouble(double l, double r) {
    // l以上r以下の実数を返す.
    return l + (r-l) * (double)rand() / RAND_MAX;
}

int randint(int l, int r) {
    // l以上r以下の整数を返す.
    return l + rand()%(r-l+1);
}


#include <algorithm>
#include <cassert>
#include <vector>

namespace atcoder {

struct dsu {
  public:
    dsu() : _n(0) {}
    explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}

    int merge(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        int x = leader(a), y = leader(b);
        if (x == y) return x;
        if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
        parent_or_size[x] += parent_or_size[y];
        parent_or_size[y] = x;
        return x;
    }

    bool same(int a, int b) {
        assert(0 <= a && a < _n);
        assert(0 <= b && b < _n);
        return leader(a) == leader(b);
    }

    int leader(int a) {
        assert(0 <= a && a < _n);
        if (parent_or_size[a] < 0) return a;
        return parent_or_size[a] = leader(parent_or_size[a]);
    }

    int size(int a) {
        assert(0 <= a && a < _n);
        return -parent_or_size[leader(a)];
    }

    std::vector<std::vector<int>> groups() {
        std::vector<int> leader_buf(_n), group_size(_n);
        for (int i = 0; i < _n; i++) {
            leader_buf[i] = leader(i);
            group_size[leader_buf[i]]++;
        }
        std::vector<std::vector<int>> result(_n);
        for (int i = 0; i < _n; i++) {
            result[i].reserve(group_size[i]);
        }
        for (int i = 0; i < _n; i++) {
            result[leader_buf[i]].push_back(i);
        }
        result.erase(
            std::remove_if(result.begin(), result.end(),
                           [&](const std::vector<int>& v) { return v.empty(); }),
            result.end());
        return result;
    }

  private:
    int _n;
    std::vector<int> parent_or_size;
};

}  // namespace atcoder

using namespace atcoder;

int calc_small(int n,vector<pair<int,int>>&edges){
  int ans=0;
  rep(bit,(1<<n)){
    int cnt=0;
    for(auto [u,v]:edges){
      if(((bit>>u)&1)!=((bit>>v)&1)){
        cnt++;
      }
    }
    ans=max(cnt,ans);
  }
  return ans;
}

double SA_start,SA_end;
double T0=5;
double T1=0.1;
double get_temp(double Time){
  return T0+(Time-SA_start)*(T0-T1)/(SA_start-SA_end);
}


int calc_big(int n,vector<vector<int>>&edge,double Time_Limit){
  int ans=0;
  int loop=3;
  rep(loop){
    struct timespec start_time;
    clock_gettime(CLOCK_REALTIME, &start_time);

    vector<int>col(n,0);
    int cnt=0;
    SA_start=0;
    SA_end=Time_Limit/loop;
    while(true){
      double now_time=stop_watch(start_time);
      if(now_time>SA_end)break;
      double temp=get_temp(now_time);
      int v=randint(0,n-1);
      int diff=0;
      for(auto u:edge[v]){
        if(col[u]!=col[v])diff--;
        else diff++;
      }
      if(diff>-10*temp&&randdouble(0,1)<exp(min(0.0,diff/temp))){
        cnt+=diff;
        ans=max(ans,cnt);
        col[v]^=1;
      }
    }
  }
  return ans;
}

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

  int n,m;
  cin>>n>>m;
  vector<vector<int>>edge(n);
  dsu uf(n);
  rep(i,m){
    int u,v;
    cin>>u>>v;
    u--;
    v--;
    edge[u].push_back(v);
    edge[v].push_back(u);
    uf.merge(u,v);
  }

  int ans=0;
  vector<int>id(n,0);

  vector<vector<vector<int>>>rem_gragh;
  int sum_rem_size=0;
  for(auto g:uf.groups()){
    if(g.size()!=1){
      int sz=g.size();
      rep(i,sz)id[g[i]]=i;
      if(sz<=15){
        vector<pair<int,int>>edges;
        for(auto v:g){
          for(auto u:edge[v]){
            if(v<u)edges.push_back({id[v],id[u]});
          }
        }
        ans+=calc_small(sz,edges);
      }
      else{
        sum_rem_size+=sz;
        vector<vector<int>>E(sz);
        for(auto v:g){
          for(auto u:edge[v]){
            E[id[v]].push_back(id[u]);
          }
        }
        rem_gragh.push_back(E);
      }
    }
  }
  if(sum_rem_size==0){
    print(ans);
    exit(0);
  }
  for(auto G:rem_gragh){
    double Time_Limit=7/(double)sum_rem_size*G.size();
    ans+=calc_big(G.size(),G,Time_Limit);
  }
  print(ans);
}
accuracy: 1
time_ms: 7000
memory_kb: 7692
final_rank: 12

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

#define ll long long
#define elif else if
#define vi vector<int>
#define vll vector<ll>
#define vvi vector<vi>
#define pii pair<int,int>


#define repname(a, b, c, d, e, ...) e
#define rep(...)                    repname(__VA_ARGS__, rep3, rep2, rep1, rep0)(__VA_ARGS__)
#define rep0(x)                     for (int rep_counter = 0; rep_counter < (x); ++rep_counter)
#define rep1(i, x)                  for (int i = 0; i < (x); ++i)
#define rep2(i, l, r)               for (int i = (l); i < (r); ++i)
#define rep3(i, l, r, c)            for (int i = (l); i < (r); i += (c))





struct ScalarInput {
    template<class T>
    operator T(){
        T ret;
        cin >> ret;
        return ret;
    }
};
struct VectorInput {
    size_t n;
    VectorInput(size_t n): n(n) {}
    template<class T>
    operator vector<T>(){
        vector<T> ret(n);
        for(T &x : ret) cin >> x;
        return ret;
    }
};
ScalarInput input(){ return ScalarInput(); }
VectorInput input(size_t n){ return VectorInput(n); }

template<typename T>
void print(vector<T> a){
  for(int i=0;i<a.size();i++){
    cout<<a[i]<<" \n"[i+1==a.size()];
  }
}

template<class T>
void print(T x){
    cout << x << '\n';
}
 
template <class Head, class... Tail>
void print(Head&& head, Tail&&... tail){
  cout << head << ' ';
  print(forward<Tail>(tail)...);
}

double stop_watch(struct timespec start_time) {
    // 経過時間を秒単位で返す.
    struct timespec end_time;
    clock_gettime(CLOCK_REALTIME, &end_time);
    long long int sec = end_time.tv_sec - start_time.tv_sec;
    long long int nsec = end_time.tv_nsec - start_time.tv_nsec;
    return (double)sec + (double)nsec / (1000 * 1000 * 1000);
}

double randdouble(double l, double r) {
    // l以上r以下の実数を返す.
    return l + (r-l) * (double)rand() / RAND_MAX;
}

int randint(int l, int r) {
    // l以上r以下の整数を返す.
    return l + rand()%(r-l+1);
}

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

  int n,m;
  cin>>n>>m;
  vector<vector<int>>edge(n);
  rep(i,m){
    int u,v;
    cin>>u>>v;
    u--;
    v--;
    edge[u].push_back(v);
    edge[v].push_back(u);
  }

  int ans=0;
  rep(8){
    struct timespec start_time;
    clock_gettime(CLOCK_REALTIME, &start_time);

    vector<int>col(n,0);
    int cnt=0;
    while(stop_watch(start_time)<0.95){
      int v=randint(0,n-1);
      int diff=0;
      for(auto u:edge[v]){
        if(col[u]!=col[v])diff--;
        else diff++;
      }
      if(diff>=0){
        cnt+=diff;
        ans=max(ans,cnt);
        col[v]^=1;
      }
    }
  }
  print(ans);
}
accuracy: 0.5789473684210527
time_ms: 7590
memory_kb: 3756
final_rank: 20

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=998244353,MAX=5005,INF=15<<26;

int N,M;
bool hen[MAX][MAX];

struct UF{
    int n;
    vector<int> par,size,edge;
    
    void init(int n_){
        n=n_;
        par.assign(n,-1);
        size.assign(n,1);
        edge.assign(n,0);
        
        for(int i=0;i<n;i++){
            par[i]=i;
        }
    }
    
    int root(int a){
        if(par[a]==a) return a;
        else return par[a]=root(par[a]);
    }
    
    void unite(int a,int b){
        edge[root(a)]++;
        if(root(a)!=root(b)){
            size[root(a)]+=size[root(b)];
            edge[root(a)]+=edge[root(b)];
            par[root(b)]=root(a);
        }
    }
    
    bool check(int a,int b){
        return root(a)==root(b);
    }
};

vector<int> solve(vector<int> V,vector<pair<int,int>> E){
    if(si(V)==0) return {0};
    if(si(V)==1) return {0,0};
    
    vector<int> id(N);
    for(int i=0;i<si(V);i++){
        id[V[i]]=i;
    }
    
    UF uf;uf.init(si(V));
    
    for(auto [a,b]:E){
        uf.unite(id[a],id[b]);
    }
    
    if(uf.size[uf.root(0)]!=si(V)){
        vector<vector<int>> pos(si(V));
        vector<vector<pair<int,int>>> EE(si(V));
        for(int a:V){
            pos[uf.root(id[a])].push_back(a);
        }
        for(auto [a,b]:E){
            EE[uf.root(id[a])].push_back(mp(a,b));
        }
        vector<int> res={0};
        for(int i=0;i<si(V);i++){
            if(si(pos[i])==0) continue;
            auto Z=solve(pos[i],EE[i]);
            vector<int> nex(si(res)+si(Z)-1);
            for(int a=0;a<si(res);a++){
                for(int b=0;b<si(Z);b++){
                    chmax(nex[a+b],res[a]+Z[b]);
                }
            }
            res=nex;
        }
        return res;
    }else{
        uf.init(si(V));
        
        for(int a:V){
            for(int b:V){
                if(a==b) continue;
                if(!hen[a][b]){
                    uf.unite(id[a],id[b]);
                }
            }
        }
        
        vector<vector<int>> pos(si(V));
        vector<vector<pair<int,int>>> EE(si(V));
        for(int a:V){
            pos[uf.root(id[a])].push_back(a);
        }
        for(auto [a,b]:E){
            if(uf.check(id[a],id[b])){
                EE[uf.root(id[a])].push_back(mp(a,b));
            }
        }
        vector<int> res={0};
        for(int i=0;i<si(V);i++){
            if(si(pos[i])==0) continue;
            auto Z=solve(pos[i],EE[i]);
            vector<int> nex(si(res)+si(Z)-1);
            for(int a=0;a<si(res);a++){
                for(int b=0;b<si(Z);b++){
                    chmax(nex[a+b],res[a]+Z[b]+a*(si(Z)-1-b)+(si(res)-1-a)*b);
                }
            }
            res=nex;
        }
        return res;
    }
}

int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    cin>>N>>M;
    vector<pair<int,int>> E(M);
    for(int i=0;i<M;i++){
        int a,b;cin>>a>>b;a--;b--;
        hen[a][b]=hen[b][a]=true;
        E[i]=mp(a,b);
    }
    vector<int> V(N);iota(all(V),0);
    auto res=solve(V,E);
    
    cout<<*max_element(all(res))<<endl;
}
accuracy: 1
time_ms: 650
memory_kb: 355196
final_rank: 13

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

using ll = long long;

struct ComplementBFS {
    static constexpr int unreachable = -1;

    ComplementBFS(int n = 0) : n(n), g(n) {}
    template <typename Edges>
    ComplementBFS(int n, const Edges &edges) : ComplementBFS(n) {
        for (const auto &[u, v] : edges) add_edge(u, v);
    }
    ComplementBFS(const std::vector<std::vector<int>>& g) : n(g.size()), g(g) {}

    void add_edge(int u, int v) {
        g[u].push_back(v);
        g[v].push_back(u);
    }

    std::vector<int> distance(const std::vector<int>& src) const {
        std::vector<int> s = [&] {
            std::vector<int8_t> is_src(n);
            for (int v : src) is_src[v] = true;
            std::vector<int> s;
            for (int i = 0; i < n; ++i) if (not is_src[i]) s.push_back(i);
            return s;
        }();

        std::vector<int> dist(n, unreachable);
        for (int v : src) dist[v] = 0;

        std::vector<int8_t> adj(n);
        std::deque<int> dq(src.begin(), src.end());
        while (dq.size()) {
            int u = dq.front();
            dq.pop_front();
            for (int v : g[u]) adj[v] = true;
            std::size_t nsiz = std::partition(s.begin(), s.end(), [&adj](int v) { return adj[v]; }) - s.begin();
            for (; s.size() > nsiz; s.pop_back()) {
                int v = s.back();
                dist[v] = dist[u] + 1, dq.push_back(v);
            }
            for (int v : g[u]) adj[v] = false;
        }
        return dist;
    }
    std::vector<int> distance(int s) const {
        return distance(std::vector<int>{ s });
    }

    std::vector<std::vector<int>> connected_components() const {
        std::vector<std::vector<int>> res;

        std::vector<int8_t> vis(n, false);

        std::vector<int> s(n);
        std::iota(s.begin(), s.end(), 0);

        std::vector<int8_t> adj(n);
        for (int i = 0; i < n; ++i) if (not vis[i]) {
            s.erase(std::find(s.begin(), s.end(), i));
            auto& cmp = res.emplace_back();
            std::deque<int> dq{ i };
            while (dq.size()) {
                int u = dq.front();
                dq.pop_front();
                cmp.push_back(u);
                vis[u] = true;
                for (int v : g[u]) adj[v] = true;
                auto it = std::partition(s.begin(), s.end(), [&adj](int v) { return adj[v]; });
                std::move(it, s.end(), std::back_inserter(dq));
                s.erase(it, s.end());
                for (int v : g[u]) adj[v] = false;
            }
        }
        return res;
    }
private:
    int n;
    std::vector<std::vector<int>> g;
};

struct BFS {
    static constexpr int unreachable = -1;

    BFS(int n = 0) : n(n), g(n) {}
    template <typename Edges>
    BFS(int n, const Edges& edges) : BFS(n) { for (const auto& [u, v] : edges) add_edge(u, v);}
    BFS(const vector<vector<int>>& g) : n(g.size()), g(g) {}

    void add_edge(int u, int v) {
        g[u].push_back(v);
        g[v].push_back(u);
    }

    vector<int> bfs(const vector<int>& src) const {
        vector<int> dist(n, unreachable);
        queue<int> q;
        for (int v : src) {
            dist[v] = 0;
            q.push(v);
        }
        while (q.size()) {
            int u = q.front();
            q.pop();
            for (int v : g[u]) if (dist[v] == unreachable) {
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
        return dist;
    }
    vector<int> bfs(int s) const { return bfs(vector<int>{ s });}

    vector<vector<int>> connected_components() const {
        vector<vector<int>> res;
        vector<int8_t> vis(n, false);
        for (int i = 0; i < n; ++i) if (not exchange(vis[i], true)) {
            auto& cmp = res.emplace_back();
            queue<int> q;
            q.push(i);
            while (q.size()) {
                int u = q.front();
                q.pop();
                cmp.push_back(u);
                for (int v : g[u]) if (not exchange(vis[v], true)) {
                    q.push(v);
                }
            }
        }
        return res;
    }
private:
    int n;
    vector<vector<int>> g;
};

int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr);
    int n, m;
    std::cin >> n >> m;

    std::vector<std::vector<int>> g(n);
    for (int i = 0; i < m; ++i) {
        int u, v;
        std::cin >> u >> v;
        --u, --v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    std::vector<int> res = [rec = [](auto rec, const std::vector<std::vector<int>> &g) -> std::vector<int> {
        const int n = g.size();
        if (n == 1) return { 0, 0 };
        auto cmps = BFS{g}.connected_components();
        if (cmps.size() == 1) {
            auto ccmps = ComplementBFS{g}.connected_components();
            assert(ccmps.size() != 1);
            std::vector<int> pd { 0 };
            std::vector<int8_t> in(n);
            std::vector<int> idx(n);
            for (const auto &cmp : ccmps) {
                const int siz = cmp.size();
                for (int i = 0; i < siz; ++i) {
                    idx[cmp[i]] = i;
                }
                std::vector<std::vector<int>> h(siz);
                for (int v : cmp) in[v] = true;
                for (int u : cmp) for (int v : g[u]) if (in[v]) {
                    h[idx[u]].push_back(idx[v]);
                }
                for (int v : cmp) in[v] = false;
                std::vector<int> val = rec(rec, h);
                const int l = pd.size() - 1, r = val.size() - 1;
                std::vector<int> dp(l + r + 1);
                for (int i = 0; i <= l; ++i) {
                    for (int j = 0; j <= r; ++j) {
                        dp[i + j] = std::max(dp[i + j], pd[i] + val[j] + i * (r - j) + (l - i) * j);
                    }
                }
                pd.swap(dp);
            }
            return pd;
        } else {
            std::vector<int> pd{ 0 };
            std::vector<int> idx(n);
            for (const auto &cmp : cmps) {
                const int siz = cmp.size();
                for (int i = 0; i < siz; ++i) idx[cmp[i]] = i;
                std::vector<std::vector<int>> h(siz);
                for (int u : cmp) for (int v : g[u]) {
                    h[idx[u]].push_back(idx[v]);
                }
                std::vector<int> val = rec(rec, h);
                const int l = pd.size() - 1, r = val.size() - 1;
                std::vector<int> dp(l + r + 1);
                for (int i = 0; i <= l; ++i) {
                    for (int j = 0; j <= r; ++j) {
                        dp[i + j] = std::max(dp[i + j], pd[i] + val[j]);
                    }
                }
                pd.swap(dp);
            }
            return pd;
        }
    }, &g]{ return rec(rec, g); }();

    std::cout << *std::max_element(res.begin(), res.end()) << std::endl;

}
accuracy: 1
time_ms: 240
memory_kb: 249108
final_rank: 10

code_snippet: #include <cassert>
#include <iostream>

#include <algorithm>
#include <cstdint>
#include <deque>
#include <numeric>
#include <utility>
#include <vector>

namespace suisen {
    struct BFS {
        static constexpr int unreachable = -1;

        BFS(int n = 0) : n(n), g(n) {}
        template <typename Edges>
        BFS(int n, const Edges& edges) : BFS(n) {
            for (const auto& [u, v] : edges) add_edge(u, v);
        }
        BFS(const std::vector<std::vector<int>>& g) : n(g.size()), g(g) {}

        void add_edge(int u, int v) {
            g[u].push_back(v);
            g[v].push_back(u);
        }

        std::vector<int> distance(const std::vector<int>& src) const {
            std::vector<int> dist(n, unreachable);
            for (int v : dist) dist[v] = 0;

            std::deque<int> dq(src.begin(), src.end());
            while (dq.size()) {
                int u = dq.front();
                dq.pop_front();
                for (int v : g[u]) if (dist[v] == unreachable) {
                    dist[v] = dist[u] + 1;
                    dq.push_back(v);
                }
            }
            return dist;
        }
        std::vector<int> distance(int s) const {
            return distance(std::vector<int>{ s });
        }

        std::vector<std::vector<int>> connected_components() const {
            std::vector<std::vector<int>> res;

            std::vector<int8_t> vis(n, false);

            for (int i = 0; i < n; ++i) if (not std::exchange(vis[i], true)) {
                auto& cmp = res.emplace_back();
                std::deque<int> dq{ i };
                while (dq.size()) {
                    int u = dq.front();
                    dq.pop_front();
                    cmp.push_back(u);
                    for (int v : g[u]) if (not std::exchange(vis[v], true)) {
                        dq.push_back(v);
                    }
                }
            }
            return res;
        }
    private:
        int n;
        std::vector<std::vector<int>> g;
    };
} // namespace suisen

namespace suisen {
    struct ComplementGraphBFS {
        static constexpr int unreachable = -1;

        ComplementGraphBFS(int n = 0) : n(n), g(n) {}
        template <typename Edges>
        ComplementGraphBFS(int n, const Edges &edges) : ComplementGraphBFS(n) {
            for (const auto &[u, v] : edges) add_edge(u, v);
        }
        ComplementGraphBFS(const std::vector<std::vector<int>>& g) : n(g.size()), g(g) {}

        void add_edge(int u, int v) {
            g[u].push_back(v);
            g[v].push_back(u);
        }

        std::vector<int> distance(const std::vector<int>& src) const {
            std::vector<int> s = [&] {
                std::vector<int8_t> is_src(n);
                for (int v : src) is_src[v] = true;
                std::vector<int> s;
                for (int i = 0; i < n; ++i) if (not is_src[i]) s.push_back(i);
                return s;
            }();

            std::vector<int> dist(n, unreachable);
            for (int v : dist) dist[v] = 0;

            std::vector<int8_t> adj(n);
            std::deque<int> dq(src.begin(), src.end());
            while (dq.size()) {
                int u = dq.front();
                dq.pop_front();
                for (int v : g[u]) adj[v] = true;
                std::size_t nsiz = std::partition(s.begin(), s.end(), [&adj](int v) { return adj[v]; }) - s.begin();
                for (; s.size() > nsiz; s.pop_back()) {
                    int v = s.back();
                    dist[v] = dist[u] + 1, dq.push_back(v);
                }
                for (int v : g[u]) adj[v] = false;
            }
            return dist;
        }
        std::vector<int> distance(int s) const {
            return distance(std::vector<int>{ s });
        }

        std::vector<std::vector<int>> connected_components() const {
            std::vector<std::vector<int>> res;

            std::vector<int8_t> vis(n, false);

            std::vector<int> s(n);
            std::iota(s.begin(), s.end(), 0);

            std::vector<int8_t> adj(n);
            for (int i = 0; i < n; ++i) if (not vis[i]) {
                s.erase(std::find(s.begin(), s.end(), i));
                auto& cmp = res.emplace_back();
                std::deque<int> dq{ i };
                while (dq.size()) {
                    int u = dq.front();
                    dq.pop_front();
                    cmp.push_back(u);
                    vis[u] = true;
                    for (int v : g[u]) adj[v] = true;
                    auto it = std::partition(s.begin(), s.end(), [&adj](int v) { return adj[v]; });
                    std::move(it, s.end(), std::back_inserter(dq));
                    s.erase(it, s.end());
                    for (int v : g[u]) adj[v] = false;
                }
            }
            return res;
        }
    private:
        int n;
        std::vector<std::vector<int>> g;
    };
} // namespace suisen

int main() {
    int n, m;
    std::cin >> n >> m;

    std::vector<std::vector<int>> g(n);
    for (int i = 0; i < m; ++i) {
        int u, v;
        std::cin >> u >> v;
        --u, --v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    std::vector<int> res = [rec = [](auto rec, const std::vector<std::vector<int>> &g) -> std::vector<int> {
        const int n = g.size();
        if (n == 1) return { 0, 0 };
        auto cmps = suisen::BFS{g}.connected_components();
        if (cmps.size() == 1) {
            auto ccmps = suisen::ComplementGraphBFS{g}.connected_components();
            assert(ccmps.size() != 1);
            std::vector<int> pd { 0 };
            std::vector<int8_t> in(n);
            std::vector<int> idx(n);
            for (const auto &cmp : ccmps) {
                const int siz = cmp.size();
                for (int i = 0; i < siz; ++i) {
                    idx[cmp[i]] = i;
                }
                std::vector<std::vector<int>> h(siz);
                for (int v : cmp) in[v] = true;
                for (int u : cmp) for (int v : g[u]) if (in[v]) {
                    h[idx[u]].push_back(idx[v]);
                }
                for (int v : cmp) in[v] = false;
                std::vector<int> val = rec(rec, h);
                const int l = pd.size() - 1, r = val.size() - 1;
                std::vector<int> dp(l + r + 1);
                for (int i = 0; i <= l; ++i) {
                    for (int j = 0; j <= r; ++j) {
                        dp[i + j] = std::max(dp[i + j], pd[i] + val[j] + i * (r - j) + (l - i) * j);
                    }
                }
                pd.swap(dp);
            }
            return pd;
        } else {
            std::vector<int> pd{ 0 };
            std::vector<int> idx(n);
            for (const auto &cmp : cmps) {
                const int siz = cmp.size();
                for (int i = 0; i < siz; ++i) idx[cmp[i]] = i;
                std::vector<std::vector<int>> h(siz);
                for (int u : cmp) for (int v : g[u]) {
                    h[idx[u]].push_back(idx[v]);
                }
                std::vector<int> val = rec(rec, h);
                const int l = pd.size() - 1, r = val.size() - 1;
                std::vector<int> dp(l + r + 1);
                for (int i = 0; i <= l; ++i) {
                    for (int j = 0; j <= r; ++j) {
                        dp[i + j] = std::max(dp[i + j], pd[i] + val[j]);
                    }
                }
                pd.swap(dp);
            }
            return pd;
        }
    }, &g]{ return rec(rec, g); }();

    std::cout << *std::max_element(res.begin(), res.end()) << std::endl;

}
accuracy: 1
time_ms: 250
memory_kb: 249252
final_rank: 11

code_snippet: #include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#define _GLIBCXX_DEBUG
using namespace std;
using std::cout;
using std::cin;
using std::endl;
using ll=long long;
using ld=long double;
ll ILL=2167167167167167167;
const int INF=2100000000;
const ll mod=998244353;
#define rep(i,a) for (int i=0;i<a;i++)
#define all(p) p.begin(),p.end()
template<class T> using _pq = priority_queue<T, vector<T>, greater<T>>;
template<class T> ll LB(vector<T> &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> ll UB(vector<T> &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> bool chmin(T &a,const T &b){if(a>b){a=b;return 1;}else return 0;}
template<class T> bool chmax(T &a,const T &b){if(a<b){a=b;return 1;}else return 0;}
template<class T> void So(vector<T> &v) {sort(v.begin(),v.end());}
template<class T> void Sore(vector<T> &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});}
void yneos(bool a){if(a) cout<<"Yes\n"; else cout<<"No\n";}
template<class T> void vec_out(vector<T> &p){for(int i=0;i<(int)(p.size());i++){if(i) cout<<" ";cout<<p[i];}cout<<"\n";}
/*
namespace po167{
struct UFtree
{
	int ind;
	int _n;
	std::vector<int> wei;
	std::vector<int> q;
	int component;
	UFtree(int n):ind(0),_n(n),wei(n),component(n),par(n){
		for(int i=0;i<n;i++){
			wei[i]=1,par[i]=i;
		}
	}
	void intialize(){
		for(auto x:q){
			wei[root(x)]=1;
			par[x]=x;
		}
		component=(int)par.size();
		q={};
	}
	void back(){
		ind--;
		int a=q[ind];
		wei[par[a]]-=wei[a];
		par[a]=a;
	}
	//根っこを返す
	int root(int a){
		assert(0<=a&&a<_n);
		if(a==par[a]) return a;
		return root(par[a]);
	}
	//trueなら1,falseなら0
	int same(int a,int b){
		assert(0<=a&&a<_n);
		assert(0<=b&&b<_n);
		if(root(a)==root(b)) return 1;
		else return 0;
	}
	//a,bが違う根っこの元なら結合する,結合したらtrueを返す
	bool unite(int a,int b){
		a=root(a),b=root(b);
		if(a==b) return false;
		if(wei[a]<wei[b]) std::swap(a,b);
		par[b]=a;
		if(ind==(int)(q.size())) q.push_back(b);
		else q[ind]=b;
		ind++;
		wei[a]+=wei[b];
		component--;
		return true;
	}
	private:
	std::vector<int> par;
};
}
using po167::UFtree;
*/

namespace po167{
struct UFtree
{
	int _n;
	std::vector<int> wei;
	std::vector<int> q;
	int component;
	UFtree(int n):_n(n),wei(n),component(n),par(n){
		for(int i=0;i<n;i++){
			wei[i]=1,par[i]=i;
		}
	}
	void intialize(){
		for(auto x:q){
			wei[root(x)]=1;
			par[x]=x;
		}
		component=(int)par.size();
		q={};
	}
	//根っこを返す
	int root(int a){
		assert(0<=a&&a<_n);
		if(a==par[a]) return a;
		return par[a]=root(par[a]);
	}
	//trueなら1,falseなら0
	int same(int a,int b){
		assert(0<=a&&a<_n);
		assert(0<=b&&b<_n);
		if(root(a)==root(b)) return 1;
		else return 0;
	}
	//a,bが違う根っこの元なら結合する,結合したらtrueを返す
	bool unite(int a,int b){
		a=root(a),b=root(b);
		if(a==b) return false;
		if(wei[a]<wei[b]) std::swap(a,b);
		par[b]=a;
		q.push_back(b);
		wei[a]+=wei[b];
		component--;
		return true;
	}
	private:
	std::vector<int> par;
};
}
using po167::UFtree;


void solve();
// oddloop
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t=1;
	//cin>>t;
	rep(i,t) solve();
}
void solve(){
	int N,M;
	cin>>N>>M;
	vector<vector<int>> G(N,vector<int>(N));
	rep(i,M){
		int a,b;
		cin>>a>>b;
		a--,b--;
		G[a][b]=1;
		G[b][a]=1;
	}
	rep(i,N) G[i][i]=-1;
	UFtree T(N);
	auto f=[&](auto self,vector<int> p,int c)->vector<int>{
		if((int)(p.size()==1)) return {0,0};
		for(auto x:p) for(auto y:p){
			if(G[x][y]==c) T.unite(x,y);
		}
		map<int,vector<int>> m;
		for(auto x:p){
			m[T.root(x)].push_back(x);
		}
		T.intialize();
		vector<int> ans={0};
		for(auto x:m){
			vector<int> q=self(self,x.second,c^1);
			vector<int> n_ans((int)(ans.size()+q.size())-1);
			rep(i,(int)(ans.size())) rep(j,(int)q.size()){
				int val=ans[i]+q[j];
				if(c==0) val+=i*((int)q.size()-j-1)+((int)(ans.size())-i-1)*j;
				chmax(n_ans[i+j],val);
			}
			swap(ans,n_ans);
		}/*
		cout<<"#\n";
		vec_out(p);
		vec_out(ans);*/
		return ans;
	};
	vector<int> in;
	rep(i,N) in.push_back(i);
	auto v=f(f,in,0);
	int ans=0;
	for(auto x:v) chmax(ans,x);
	cout<<ans<<"\n";
}
accuracy: 1
time_ms: 490
memory_kb: 104684
final_rank: 5

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 1000000000000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define SIZE 5005

struct Info{
	void set(int arg_minimum,int arg_maximum){
		minimum = arg_minimum;
		maximum = arg_maximum;
	}
	int minimum,maximum;
};

int N,E;
bool table[SIZE][SIZE];
int boss[SIZE],height[SIZE],num_member[SIZE];

int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[boss_x] > height[boss_y]){

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;

	}else if(height[boss_x] < height[boss_y]){

		num_member[boss_y] += num_member[boss_x];
		boss[boss_x] = boss_y;

	}else{ //height[boss_x] == height[boss_y]

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void init(int num){

	for(int i = 0; i < num; i++){
		boss[i] = i;
		height[i] = 0;
		num_member[i] = 1;
	}
}

vector<Info> get_info(int size){

	vector<Info> ret(size);

	for(int i = 0; i < size; i++){

		ret[i].set(0,0);
	}

	return ret;
}

vector<Info> func(vector<Info> A,vector<Info> B){

	int num_all = A.size()+B.size()-1;

	vector<Info> ret(num_all);
	for(int i = 0; i < num_all; i++){

		ret[i].set(BIG_NUM,-BIG_NUM);
	}

	for(int i = 0; i < A.size(); i++){
		for(int k = 0; k < B.size(); k++){

			ret[i+k].minimum = min(ret[i+k].minimum,A[i].minimum+B[k].minimum);
			ret[i+k].maximum = max(ret[i+k].maximum,A[i].maximum+B[k].maximum);
		}
	}

	return ret;
}

vector<Info> func_rev(vector<Info> A,vector<Info> B){

	int num_all = A.size()+B.size()-1;
	int tmp_num = (int)(A.size()+B.size());

	vector<Info> ret(num_all);

	for(int i = 0; i < num_all; i++){

		ret[i].set(BIG_NUM,-BIG_NUM);
	}

	for(int i = 0; i < A.size(); i++){
		for(int k = 0; k < B.size(); k++){

			ret[i+k].minimum = min(ret[i+k].minimum,(i+k)*(tmp_num-2-(i+k))-(A[i].maximum+B[k].maximum));
			ret[i+k].maximum = max(ret[i+k].maximum,(i+k)*(tmp_num-2-(i+k))-(A[i].minimum+B[k].minimum));
		}
	}

	return ret;
}


vector<Info> recursive(vector<int> V,bool reversed){

	int NUM = V.size();

	if(NUM == 1){

		return get_info(2);
	}

	init(NUM);

	for(int i = 0; i < NUM-1; i++){
		for(int k = i+1; k < NUM; k++){
			if((!reversed&&table[V[i]][V[k]])||(reversed&&!table[V[i]][V[k]])){

				unite(i,k);
			}
		}
	}

	int maximum = -1;
	for(int i = 0; i < NUM; i++){

		maximum = max(maximum,num_member[get_boss(i)]);
	}

	vector<int> WORK[NUM];


	if(maximum == NUM){

		init(NUM);

		for(int i = 0; i < NUM-1; i++){
			for(int k = i+1; k < NUM; k++){
				if((!reversed&&!table[V[i]][V[k]])||(reversed&&table[V[i]][V[k]])){

					unite(i,k);
				}
			}
		}

		int count = 0;
		int another = 0;

		for(int i = 0; i < NUM; i++){
			if(num_member[get_boss(i)] == 1){

				count++;
			}else{

				WORK[get_boss(i)].push_back(V[i]);
				another++;
			}
		}

		if(another > 0){

			vector<Info> ret = get_info(1);

			for(int i = 0; i < NUM; i++){
				if(WORK[i].size() == 0)continue;

				ret = func(ret,recursive(WORK[i],not reversed));
			}

			return func_rev(ret,get_info(count+1));

		}else{

			vector<Info> ret = get_info(count+1);

			return func_rev(ret,get_info(1));
		}

	}else{

		int count = 0;

		for(int i = 0; i < NUM; i++){
			if(num_member[get_boss(i)] == 1){

				count++;
			}else{

				WORK[get_boss(i)].push_back(V[i]);
			}
		}

		vector<Info> ret = get_info(count+1);

		for(int i = 0; i < NUM; i++){
			if(WORK[i].size() == 0)continue;

			ret = func(ret,recursive(WORK[i],reversed));
		}

		return ret;
	}
}


int main(){

	scanf("%d %d",&N,&E);

	for(int i = 0; i < N; i++){
		for(int k = 0; k < N; k++){

			table[i][k] = false;
		}
	}

	init(N);

	int from,to;
	for(int i = 0; i < E; i++){

		scanf("%d %d",&from,&to);
		from--;
		to--;
		table[from][to] = true;
		table[to][from] = true;

		unite(from,to);
	}

	vector<int> first_V;
	for(int i = 0; i < N; i++){

		first_V.push_back(i);
	}

	vector<Info> ret = recursive(first_V,false);

	int ans = -1;
	for(int i = 0; i < N; i++){

		ans = max(ans,ret[i].maximum);
	}

	printf("%d\n",ans);

	return 0;
}
accuracy: 1
time_ms: 580
memory_kb: 34068
final_rank: 3

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX * 2][MAX]={};
int len[MAX * 2]={};

vector<int> G[MAX];
int blg[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);
      rmv(find(nxt.begin(),nxt.end(),r)-nxt.begin());

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 122920
final_rank: 6

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);
      rmv(find(nxt.begin(),nxt.end(),r)-nxt.begin());

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 0.5789473684210527
time_ms: 30
memory_kb: 68960
final_rank: 14

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=1;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);
      rmv(find(nxt.begin(),nxt.end(),r)-nxt.begin());

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 123044
final_rank: 8

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}
template<typename T> void drop(const T &x){cout<<x<<endl;exit(0);}
template<typename T=int>
vector<T> read(size_t n){
  vector<T> ts(n);
  for(size_t i=0;i<n;i++) cin>>ts[i];
  return ts;
}


vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=1;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      assert(count(nxt.begin(),nxt.end(),r)==1);
      {
        int num=0;
        for(int k=0;k<(int)nxt.size();k++)
          if(nxt[k]==r) rmv(k),num++,k--;
        assert(num==1);
      }

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    // assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];
  if(0){
    for(int i=0;i<(int)C.size();i++)
      for(int j=0;j<i;j++)
        for(int x:C[i])
          for(int y:C[j])
            assert(exist==es[x][y]);
  }

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);
  assert(idx==1);
  // assert(len[idx]==n);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 122992
final_rank: 7

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}
template<typename T> void drop(const T &x){cout<<x<<endl;exit(0);}
template<typename T=int>
vector<T> read(size_t n){
  vector<T> ts(n);
  for(size_t i=0;i<n;i++) cin>>ts[i];
  return ts;
}


vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      assert(count(nxt.begin(),nxt.end(),r)==1);
      {
        int num=0;
        for(int k=0;k<(int)nxt.size();k++)
          if(nxt[k]==r) rmv(k),num++,k--;
        assert(num==1);
      }

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    // assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];
  if(0){
    for(int i=0;i<(int)C.size();i++)
      for(int j=0;j<i;j++)
        for(int x:C[i])
          for(int y:C[j])
            assert(exist==es[x][y]);
  }

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);
  assert(len[idx]<=n);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 0.5789473684210527
time_ms: 40
memory_kb: 69028
final_rank: 16

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}
template<typename T> void drop(const T &x){cout<<x<<endl;exit(0);}
template<typename T=int>
vector<T> read(size_t n){
  vector<T> ts(n);
  for(size_t i=0;i<n;i++) cin>>ts[i];
  return ts;
}


vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      assert(count(nxt.begin(),nxt.end(),r)==1);
      {
        int num=0;
        for(int k=0;k<(int)nxt.size();k++)
          if(nxt[k]==r) rmv(k),num++,k--;
        assert(num==1);
      }

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    // assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];
  if(0){
    for(int i=0;i<(int)C.size();i++)
      for(int j=0;j<i;j++)
        for(int x:C[i])
          for(int y:C[j])
            assert(exist==es[x][y]);
  }

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);
  assert(len[idx]==n);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 0.5789473684210527
time_ms: 40
memory_kb: 68988
final_rank: 15

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}
template<typename T> void drop(const T &x){cout<<x<<endl;exit(0);}
template<typename T=int>
vector<T> read(size_t n){
  vector<T> ts(n);
  for(size_t i=0;i<n;i++) cin>>ts[i];
  return ts;
}


vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      assert(count(nxt.begin(),nxt.end(),r)==1);
      {
        int num=0;
        for(int k=0;k<(int)nxt.size();k++)
          if(nxt[k]==r) rmv(k),num++,k--;
        assert(num==1);
      }

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    // assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];
  if(0){
    for(int i=0;i<(int)C.size();i++)
      for(int j=0;j<i;j++)
        for(int x:C[i])
          for(int y:C[j])
            assert(exist==es[x][y]);
  }

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);
// assert(len[idx]==n);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 0.5789473684210527
time_ms: 40
memory_kb: 69144
final_rank: 19

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}
template<typename T> void drop(const T &x){cout<<x<<endl;exit(0);}
template<typename T=int>
vector<T> read(size_t n){
  vector<T> ts(n);
  for(size_t i=0;i<n;i++) cin>>ts[i];
  return ts;
}


vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      assert(count(nxt.begin(),nxt.end(),r)==1);
      for(int k=0;k<(int)nxt.size();k++)
        if(nxt[k]==r) rmv(k);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    // assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];
  if(0){
    for(int i=0;i<(int)C.size();i++)
      for(int j=0;j<i;j++)
        for(int x:C[i])
          for(int y:C[j])
            assert(exist==es[x][y]);
  }

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);
// assert(len[idx]==n);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 0.5789473684210527
time_ms: 40
memory_kb: 69072
final_rank: 18

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

using Int = long long;
const char newl = '\n';

template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}
template<typename T> void drop(const T &x){cout<<x<<endl;exit(0);}
template<typename T=int>
vector<T> read(size_t n){
  vector<T> ts(n);
  for(size_t i=0;i<n;i++) cin>>ts[i];
  return ts;
}


vector<int> identity(int n){
  vector<int> ord(n);
  iota(ord.begin(),ord.end(),0);
  return ord;
}

//INSERT ABOVE HERE
const int MAX = 5050;
int es[MAX][MAX]={};

int cur=0;
int dp[MAX][MAX]={};

vector<int> G[MAX];
int blg[MAX]={};
int len[MAX]={};
int used[MAX]={};

int dfs(vector<int> vs){
  int idx=cur++;
  len[idx]=vs.size();

  if(vs.size()==1){
    dp[idx][0]=dp[idx][1]=0;
    return idx;
  }

  vector<vector<int>> C;
  queue<int> que;
  auto push=[&](int v){
    if(used[v]) return;
    used[v]=1;
    que.emplace(v);
    C.back().emplace_back(v);
  };

  // check connectivity
  {
    // black
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int u:G[v]){
          if(blg[u]!=blg[v]) continue;
          push(u);
        }
      }
    };

    for(int v:vs) blg[v]=idx,used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);
    for(int v:vs) blg[v]=-1;
  }

  if(C.size()==1){
    C.clear();

    vector<int> nxt=vs;
    auto rmv=[&](int k){
      assert(0<=k and k<(int)nxt.size());
      if(nxt[k]!=nxt.back()) swap(nxt[k],nxt.back());
      nxt.pop_back();
    };

    // white
    auto bfs=[&](int r){
      C.emplace_back();
      push(r);
      for(int k=0;k<(int)nxt.size();k++)
        if(nxt[k]==r) rmv(k);

      while(!que.empty()){
        int v=que.front();que.pop();
        for(int k=0;k<(int)nxt.size();k++){
          int u=nxt[k];
          if(es[v][u]) continue;
          push(u);
          rmv(k);
          k--;
        }
      }
    };

    for(int v:vs) used[v]=0;
    for(int v:vs) if(!used[v]) bfs(v);

    // assert(C.size()!=1);
  }

  vector<int> I;
  for(auto cs:C)
    I.emplace_back(dfs(cs));

  int exist=es[C[0][0]][C[1][0]];
  if(0){
    for(int i=0;i<(int)C.size();i++)
      for(int j=0;j<i;j++)
        for(int x:C[i])
          for(int y:C[j])
            assert(exist==es[x][y]);
  }

  int sz=0;
  dp[idx][0]=0;
  for(int pos:I){
    int nx=len[pos];
    vector<int> tmp(sz+nx+1,0);
    for(int i=0;i<=sz;i++){
      for(int j=0;j<=nx;j++){
        int way=exist*(i*(nx-j)+j*(sz-i));
        chmax(tmp[i+j],dp[idx][i]+dp[pos][j]+way);
      }
    }
    sz+=nx;
    for(int i=0;i<=sz;i++) dp[idx][i]=tmp[i];
  }

  return idx;
}

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

  int n,m;
  cin>>n>>m;
  for(int i=0;i<m;i++){
    int u,v;
    cin>>u>>v;
    u--;v--;
    es[u][v]=es[v][u]=1;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  }

  memset(blg,-1,sizeof(blg));

  auto vs=identity(n);
  int idx=dfs(vs);
// assert(len[idx]==n);

  int ans=0;
  for(int i=0;i<=n;i++) chmax(ans,dp[idx][i]);
  cout<<ans<<newl;
  return 0;
}
accuracy: 0.5789473684210527
time_ms: 40
memory_kb: 69068
final_rank: 17