#include #define REP(i, n) for (int i = 0; (i) < (int)(n); ++ (i)) using namespace std; template inline void chmin(T & a, U const & b) { a = min(a, b); } /** * @brief sparse table on a semilattice * @note a semilattice is a commutative idempotent semigroup * @note for convenience, it requires the unit * @note O(N log N) space */ template struct sparse_table { typedef typename Semilattice::value_type value_type; std::vector > table; Semilattice lat; sparse_table() = default; /** * @note O(N \log N) time */ sparse_table(std::vector const & data, Semilattice const & a_lat = Semilattice()) : lat(a_lat) { int n = data.size(); int log_n = 32 - __builtin_clz(n); table.resize(log_n, std::vector(n)); table[0] = data; REP (k, log_n - 1) { REP (i, n) { table[k + 1][i] = i + (1ll << k) < n ? lat.append(table[k][i], table[k][i + (1ll << k)]) : table[k][i]; } } } /** * @note O(1) */ value_type range_concat(int l, int r) const { if (l == r) return lat.unit(); // if there is no unit, remove this line assert (0 <= l and l < r and r <= table[0].size()); int k = 31 - __builtin_clz(r - l); // log2 return lat.append(table[k][l], table[k][r - (1ll << k)]); } }; struct indexed_min_semilattice { typedef std::pair value_type; value_type unit() const { return { INT_MAX, INT_MAX }; } value_type append(value_type a, value_type b) const { return std::min(a, b); } }; /** * @brief lowest common ancestor with \pm 1 RMQ and sparse table * @see https://www.slideshare.net/yumainoue965/lca-and-rmq * @note verified http://www.utpc.jp/2011/problems/travel.html */ struct lowest_common_ancestor_for_forest { sparse_table table; std::vector index; lowest_common_ancestor_for_forest() = default; /** * @note O(N) * @param g is an adjacent list of a tree */ lowest_common_ancestor_for_forest(std::vector > const & g) { std::vector > tour; index.assign(g.size(), -1); REP (root, g.size()) if (index[root] == -1) { dfs(root, -1, 0, g, tour); tour.emplace_back(-1, -1); } table = sparse_table(tour); } private: /** * @note to avoid stack overflow */ void dfs(int x, int parent, int depth, std::vector > const & g, std::vector > & tour) { index[x] = tour.size(); tour.emplace_back(depth, x); for (int y : g[x]) if (y != parent) { dfs(y, x, depth + 1, g, tour); tour.emplace_back(depth, x); } } public: /** * @note O(1) */ int operator () (int x, int y) const { assert (0 <= x and x < index.size()); assert (0 <= y and y < index.size()); x = index[x]; y = index[y]; if (x > y) std::swap(x, y); return table.range_concat(x, y + 1).second; } int get_depth(int x) const { assert (0 <= x and x < index.size()); return table.range_concat(index[x], index[x] + 1).first; } bool is_in_same_tree(int x, int y) const { return (*this)(x, y) != -1; } int get_dist(int x, int y) const { assert (0 <= x and x < index.size()); assert (0 <= y and y < index.size()); int z = (*this)(x, y); return get_depth(x) + get_depth(y) - 2 * get_depth(z); } }; int64_t solve(int n, int m, int q, const vector > & g, const vector & a, const vector & b) { lowest_common_ancestor_for_forest lca(g); int64_t answer = 0; vector cnt(n); REP (i, q) { if (lca.is_in_same_tree(a[i], b[i])) { answer += lca.get_dist(a[i], b[i]); } else { ++ cnt[a[i]]; ++ cnt[b[i]]; } } vector imos(n); REP (root, n) if (lca.get_depth(root) == 0) { int sum_cnt = 0; function go1 = [&](int x, int parent, int depth) { sum_cnt += cnt[x]; imos[root] += depth * cnt[x]; int k = cnt[x]; for (int y : g[x]) if (y != parent) { k += go1(y, x, depth + 1); } if (x != root) { imos[x] += - 2 * k; } return k; }; go1(root, -1, 0); int64_t min_cost = INT64_MAX; function go2 = [&](int x, int parent) { chmin(min_cost, imos[x]); for (int y : g[x]) if (y != parent) { imos[y] += imos[x] + sum_cnt; go2(y, x); } }; go2(root, -1); answer += min_cost; } return answer; } int main() { int n, m, q; cin >> n >> m >> q; vector > g(n); REP (i, m) { int u, v; cin >> u >> v; -- u; -- v; g[u].push_back(v); g[v].push_back(u); } vector a(q), b(q); REP (i, q) { cin >> a[i] >> b[i]; -- a[i]; -- b[i]; } cout << solve(n, m, q, g, a, b) << endl; return 0; }