#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
class DisjointSet {
	// Union-Find Tree
	int sz;
	vector<int> par;
public:
	DisjointSet() : sz(0), par(vector<int>()) {};
	DisjointSet(int sz_) : sz(sz_) {
		par = vector<int>(sz_);
		for (int i = 0; i < sz_; ++i) {
			par[i] = i;
		}
	};
	int root(int x) {
		if (x == par[x]) return x;
		return par[x] = root(par[x]);
	}
	void unite(int x, int y) {
		par[root(x)] = root(y);
	}
	bool connected(int x, int y) {
		return root(x) == root(y);
	}
};
int medival(vector<int> v) {
	// Let X the nearest subset sum to half, returns X * (sum - X)
	int sum = 0;
	for (int i = 0; i < v.size(); ++i) {
		sum += v[i];
	}
	vector<bool> vis(sum + 1, false);
	vis[0] = true;
	for (int i = 0; i < v.size(); ++i) {
		for (int j = sum; j >= v[i]; --j) {
			if (vis[j - v[i]]) {
				vis[j] = true;
			}
		}
	}
	for (int i = sum / 2; i >= 0; --i) {
		if (vis[i]) return i * (sum - i);
	}
	return -1;
}
int solve(int N, vector<int> a, vector<int> b) {
	// The graph is connected
	int M = a.size();
	DisjointSet all(N);
	vector<bool> bridge(M, false);
	vector<int> va, vb;
	for (int i = 0; i < M; ++i) {
		DisjointSet d(N);
		for (int j = 0; j < M; ++j) {
			if (i != j) {
				d.unite(a[j], b[j]);
			}
		}
		for (int j = 1; j < N; ++j) {
			if (!d.connected(0, j)) bridge[i] = true;
		}
		if (!bridge[i]) {
			all.unite(a[i], b[i]);
		}
		else {
			va.push_back(a[i]);
			vb.push_back(b[i]);
		}
	}
	vector<int> vli;
	for (int i = 0; i < N; ++i) {
		vli.push_back(all.root(i));
	}
	sort(vli.begin(), vli.end());
	vli.erase(unique(vli.begin(), vli.end()), vli.end());
	vector<int> weight(vli.size());
	vector<vector<int> > tree(vli.size());
	for (int i = 0; i < va.size(); ++i) {
		va[i] = find(vli.begin(), vli.end(), all.root(va[i])) - vli.begin();
		vb[i] = find(vli.begin(), vli.end(), all.root(vb[i])) - vli.begin();
		tree[va[i]].push_back(vb[i]);
		tree[vb[i]].push_back(va[i]);
	}
	for (int i = 0; i < N; ++i) {
		++weight[find(vli.begin(), vli.end(), all.root(i)) - vli.begin()];
	}
	function<pair<int, int>(int, int)> calc = [&](int pos, int pre) {
		int sum = 0, ord = 0;
		for (int i : tree[pos]) {
			if (i == pre) continue;
			pair<int, int> res = calc(i, pos);
			ord += res.second;
			sum += res.first - res.second * res.second + weight[pos] * res.second;
		}
		return make_pair(ord * ord + sum, ord + weight[pos]);
	};
	int ret = 0;
	for (int i = 0; i < vli.size(); ++i) {
		int sum = weight[i] * weight[i]; // <-- CENTER
		vector<int> sz;
		for (int j : tree[i]) {
			pair<int, int> res = calc(j, i);
			sum += res.second * res.second - res.first + res.second * weight[i];
			sz.push_back(res.second);
		}
		sum += medival(sz);
		ret = max(ret, sum);
	}
	return ret;
}
int main() {
	cin.tie(0);
	ios_base::sync_with_stdio(false);
	int N, M;
	cin >> N >> M;
	vector<string> s(N);
	for (int i = 0; i < N; ++i) {
		cin >> s[i];
	}
	vector<int> va(M), vb(M);
	for (int i = 0; i < M; ++i) {
		string sa, sb;
		cin >> sa >> sb;
		va[i] = find(s.begin(), s.end(), sa) - s.begin();
		vb[i] = find(s.begin(), s.end(), sb) - s.begin();
	}
	DisjointSet d(N);
	for (int i = 0; i < M; ++i) {
		d.unite(va[i], vb[i]);
	}
	int ret = 0;
	for (int i = 0; i < N; ++i) {
		vector<int> vli;
		for (int j = 0; j < N; ++j) {
			if (d.root(j) == i) {
				vli.push_back(j);
			}
		}
		if (vli.empty()) continue;
		vector<int> ca, cb;
		for (int j = 0; j < M; ++j) {
			if (d.root(va[j]) == i) {
				ca.push_back(find(vli.begin(), vli.end(), va[j]) - vli.begin());
				cb.push_back(find(vli.begin(), vli.end(), vb[j]) - vli.begin());
			}
		}
		ret += solve(vli.size(), ca, cb);
	}
	cout << ret << '\n';
	return 0;
}


Measured memory_bytes: 3360