/** * code generated by JHelper * More info: https://github.com/AlexeyDmitriev/JHelper * @author Egor Kulikov */ #include #include #ifndef JHELPER_EXAMPLE_PROJECT_INPUT_H #define JHELPER_EXAMPLE_PROJECT_INPUT_H // // Created by egor on 30.10.2019. // #ifndef JHELPER_EXAMPLE_PROJECT_GENERAL_H #define JHELPER_EXAMPLE_PROJECT_GENERAL_H #include using namespace std; #define all(v) (v).begin(), (v).end() typedef long long ll; typedef vector vi; typedef pair pii; const int MAX_INT = 2147483647; const double PI = atan(1) * 4; const int DX_KNIGHT[] = {2, 1, -1, -2, -2, -1, 1, 2}; const int DY_KNIGHT[] = {1, 2, 2, 1, -1, -2, -2, -1}; const int DX4[] = {1, 0, -1, 0}; const int DY4[] = {0, 1, 0, -1}; bool isValidCell(int x, int y, int rows, int cols) { return x >= 0 && y >= 0 && x < rows && y < cols; } void decreaseByOne() { } template void decreaseByOne(vector& arr, Vargs&...arrs) { for (int& i : arr) { i--; } decreaseByOne(arrs...); } template T minim(T& was, T cand) { return was = min(was, cand); } template T maxim(T& was, T cand) { return was = max(was, cand); } inline int bitCount(int number) { return __builtin_popcount(number); } inline int bitCount(ll number) { return __builtin_popcount(number); } #endif //JHELPER_EXAMPLE_PROJECT_GENERAL_H class Input { public: enum ErrorType { OK, END_OF_FILE, UNEXPECTED_SYMBOL, INVALID_CALL }; private: istream& in; bool exhausted = false; ErrorType error = OK; int get(); template T readInteger(); int skipWhitespace(); public: Input(istream& in); int readInt(); ll readLong(); string readString(); vector readIntArray(int size); template T readType(); template pair readType(); template vector readArray(int size); template tuple, vector > readArrays(int size); template tuple, vector, vector > readArrays(int size); template tuple, vector, vector, vector > readArrays(int size); template tuple, vector, vector, vector, vector > readArrays(int size); template vector > readTable(int rows, int cols); string readLine(); }; inline bool isWhitespace(int c) { return isspace(c) || c == EOF; } int Input::skipWhitespace() { int c; do { c = get(); if (exhausted) { error = END_OF_FILE; return c; } } while (isWhitespace(c)); return c; } Input::Input(std::istream &in) : in(in) { } inline int Input::get() { int result = in.get(); if (result == EOF) { exhausted = true; } return result; } template T Input::readType() { error = INVALID_CALL; return nullptr; } template T Input::readInteger() { error = OK; int c = skipWhitespace(); if (error != OK) { return 0; } int sgn = 1; if (c == '-') { sgn = -1; c = get(); } T res = 0; do { if (!isdigit(c)) { error = UNEXPECTED_SYMBOL; return 0; } res *= 10; res += c - '0'; c = get(); } while (!isWhitespace(c)); return res * sgn; } template<> int Input::readType() { return readInteger(); } template<> ll Input::readType() { return readInteger(); } template<> char Input::readType() { error = OK; int c = skipWhitespace(); if (error != OK) { return 0; } return c; } template<> string Input::readType() { error = OK; int c = skipWhitespace(); if (error != OK) { return ""; } vector res; do { if (error != OK) { return ""; } res.push_back(c); } while (!isWhitespace(c = get())); return string(res.begin(), res.end()); } inline int Input::readInt() { return readType(); } inline ll Input::readLong() { return readType(); } template vector Input::readArray(int size) { vector res; res.reserve(size); for (int i = 0; i < size; i++) { res.push_back(readType()); if (error != OK) { res.clear(); return res; } } return res; } vector Input::readIntArray(int size) { return readArray(size); } template tuple, vector > Input::readArrays(int size) { vector v1; vector v2; v1.reserve(size); v2.reserve(size); for (int i = 0; i < size; ++i) { v1.push_back(readType()); v2.push_back(readType()); } return make_tuple(v1, v2); } string Input::readString() { return readType(); } template vector> Input::readTable(int rows, int cols) { vector > result; result.reserve(rows); for (int i = 0; i < rows; ++i) { result.push_back(readArray(cols)); } return result; } string Input::readLine() { error = OK; int c = skipWhitespace(); if (error != OK) { return ""; } int length = 0; vector res; do { if (error != OK) { return ""; } res.push_back(c); if (!isWhitespace(c)) { length = res.size(); } c = get(); } while (c != '\n' && c != '\r' && c != EOF); return string(res.begin(), res.begin() + length); } template tuple, vector, vector > Input::readArrays(int size) { vector v1; vector v2; vector v3; v1.reserve(size); v2.reserve(size); v3.reserve(size); for (int i = 0; i < size; ++i) { v1.push_back(readType()); v2.push_back(readType()); v3.push_back(readType()); } return make_tuple(v1, v2, v3); } template tuple, vector, vector, vector > Input::readArrays(int size) { vector v1; vector v2; vector v3; vector v4; v1.reserve(size); v2.reserve(size); v3.reserve(size); v4.reserve(size); for (int i = 0; i < size; ++i) { v1.push_back(readType()); v2.push_back(readType()); v3.push_back(readType()); v4.push_back(readType()); } return make_tuple(v1, v2, v3, v4); } template tuple, vector, vector, vector, vector > Input::readArrays(int size) { vector v1; vector v2; vector v3; vector v4; vector v5; v1.reserve(size); v2.reserve(size); v3.reserve(size); v4.reserve(size); v5.reserve(size); for (int i = 0; i < size; ++i) { v1.push_back(readType()); v2.push_back(readType()); v3.push_back(readType()); v4.push_back(readType()); v5.push_back(readType()); } return make_tuple(v1, v2, v3, v4, v5); } #endif //JHELPER_EXAMPLE_PROJECT_INPUT_H // // Created by egor on 31.10.2019. // #ifndef JHELPER_EXAMPLE_PROJECT_OUTPUT_H #define JHELPER_EXAMPLE_PROJECT_OUTPUT_H class Output { private: ostream& out; template void printSingle(const T& value); template void printSingle(const vector& value); template void printSingle(const pair& value); public: Output(ostream& out); void print(); templatevoid print(const T& first, const Targs... args); templatevoid printLine(const Targs... args); void flush(); }; Output::Output(ostream &out) : out(out) { out << setprecision(12); } void Output::print() { } template void Output::print(const T& first, const Targs... args) { printSingle(first); if (sizeof...(args) != 0) { out << ' '; print(args...); } } template void Output::printSingle(const T& value) { out << value; } template void Output::printLine(const Targs... args) { print(args...); out << '\n'; } void Output::flush() { out.flush(); } template void Output::printSingle(const vector& array) { unsigned int size = array.size(); for (int i = 0; i < size; ++i) { out << array[i]; if (i + 1 != size) { out << ' '; } } } template void Output::printSingle(const pair& value) { out << value.first << ' ' << value.second; } #endif //JHELPER_EXAMPLE_PROJECT_OUTPUT_H // // Created by egor on 31.10.2019. // #ifndef JHELPER_EXAMPLE_PROJECT_DSU_H #define JHELPER_EXAMPLE_PROJECT_DSU_H // // Created by egor on 31.10.2019. // #ifndef JHELPER_EXAMPLE_PROJECT_ALGO_H #define JHELPER_EXAMPLE_PROJECT_ALGO_H template inline void unique(vector& v) { v.resize(unique(all(v)) - v.begin()); } vi createOrder(int n) { vi order(n); for (int i = 0; i < n; i++) { order[i] = i; } return order; } template inline vector > makeArray(int a, int b) { return vector >(a, vector(b)); } //template //inline vector > > makeArray(int a, int b, int c) { // return vector > >(a, makeArray(b, c)); //} template inline vector > makeArray(int a, int b, T init) { return vector >(a, vector(b, init)); } template inline vector > > makeArray(int a, int b, int c, T init) { return vector > >(a, makeArray(b, c, init)); } template inline void addAll(vector& v, const vector& toAdd) { v.insert(v.end(), toAdd.begin(), toAdd.end()); } #endif //JHELPER_EXAMPLE_PROJECT_ALGO_H class DSU { private: vi id; vi size; int setCount; public: DSU(int n); int get(int i); int getSize(int i) { return size[get(i)]; } int getSetCount() { return setCount; } bool join(int a, int b); }; DSU::DSU(int n) { id = createOrder(n); size = vi(n, 1); setCount = n; } int DSU::get(int i) { if (id[i] == i) { return i; } return id[i] = get(id[i]); } bool DSU::join(int a, int b) { a = get(a); b = get(b); if (a == b) { return false; } size[a] += size[b]; size[b] = 0; id[b] = a; setCount--; return true; } #endif //JHELPER_EXAMPLE_PROJECT_DSU_H // // Created by egor on 04.11.2019. // #ifndef JHELPER_EXAMPLE_PROJECT_GRAPH_H #define JHELPER_EXAMPLE_PROJECT_GRAPH_H template class WeightedFlowEdge { private: WeightedFlowEdge* reverseEdge; public: const int from; const int to; W weight; C capacity; int id; WeightedFlowEdge(int from, int to, W weight, C capacity) : from(from), to(to), weight(weight), capacity(capacity) { reverseEdge = new WeightedFlowEdge(this); } WeightedFlowEdge* transposed() { return nullptr; } WeightedFlowEdge* reverse() { return reverseEdge; } void push(C flow) { capacity -= flow; reverseEdge->capacity += flow; } C flow() const { return reverseEdge->capacity; } private: WeightedFlowEdge(WeightedFlowEdge* reverse) : from(reverse->to), to(reverse->from), weight(-reverse->weight), capacity(0) { reverseEdge = reverse; } }; template class FlowEdge { private: FlowEdge* reverseEdge; public: const int from; const int to; C capacity; int id; FlowEdge(int from, int to, C capacity) : from(from), to(to), capacity(capacity) { reverseEdge = new FlowEdge(this); } FlowEdge* transposed() { return nullptr; } FlowEdge* reverse() { return reverseEdge; } void push(C flow) { capacity -= flow; reverseEdge->capacity += flow; } C flow() const { return reverseEdge->capacity; } private: FlowEdge(FlowEdge* reverse) : from(reverse->to), to(reverse->from), capacity(0) { reverseEdge = reverse; } }; template class WeightedEdge { public: const int from; const int to; W weight; int id; WeightedEdge(int from, int to, W weight) : from(from), to(to), weight(weight) { } WeightedEdge* transposed() { return nullptr; } WeightedEdge* reverse() { return nullptr; } }; template class BidirectionalWeightedEdge { private: BidirectionalWeightedEdge* transposedEdge; public: const int from; const int to; W weight; int id; BidirectionalWeightedEdge(int from, int to, W weight) : from(from), to(to), weight(weight) { transposedEdge = new BidirectionalWeightedEdge(this); } BidirectionalWeightedEdge* transposed() { return transposedEdge; } BidirectionalWeightedEdge* reverse() { return nullptr; } private: BidirectionalWeightedEdge(BidirectionalWeightedEdge* transposed) : from(transposed->to), to(transposed->from), weight(transposed->weight) { transposedEdge = transposed; } }; class SimpleEdge { public: const int from; const int to; int id; SimpleEdge(int from, int to) : from(from), to(to) { } SimpleEdge* transposed() { return nullptr; } SimpleEdge* reverse() { return nullptr; } }; class BidirectionalEdge { private: BidirectionalEdge* transposedEdge; public: const int from; const int to; int id; BidirectionalEdge(int from, int to) : from(from), to(to) { transposedEdge = new BidirectionalEdge(this); } BidirectionalEdge* transposed() { return transposedEdge; } BidirectionalEdge* reverse() { return nullptr; } private: BidirectionalEdge(BidirectionalEdge* transposed) : from(transposed->to), to(transposed->from) { transposedEdge = transposed; } }; template class Graph { public: int vertexCount; int edgeCount = 0; vector > edges; Graph(int vertexCount) : vertexCount(vertexCount) { edges.resize(vertexCount); } void addEdge(Edge* edge) { edge->id = edgeCount; edges[edge->from].push_back(edge); if (edge->reverse() != nullptr) { edge->reverse()->id = edgeCount; edges[edge->reverse()->from].push_back(edge->reverse()); } if (edge->transposed() != nullptr) { edges[edge->transposed()->from].push_back(edge->transposed()); edge->transposed()->id = edgeCount; if (edge->transposed()->reverse() != nullptr) { edges[edge->transposed()->reverse()->from].push_back(edge->transposed()->reverse()); edge->transposed()->reverse()->id = edgeCount; } } edgeCount++; } }; typedef FlowEdge LongFlowEdge; typedef WeightedEdge LongWeightedEdge; typedef FlowEdge IntFlowEdge; typedef WeightedEdge IntWeightedEdge; typedef BidirectionalWeightedEdge LongBiWeightedEdge; typedef BidirectionalWeightedEdge IntBiWeightedEdge; #endif //JHELPER_EXAMPLE_PROJECT_GRAPH_H #ifndef CPP_LCA_H #define CPP_LCA_H // // Created by kulikov on 11/7/2019. // #include #ifndef JHELPER_EXAMPLE_PROJECT_INTERVAL_TREE_H #define JHELPER_EXAMPLE_PROJECT_INTERVAL_TREE_H template class IntervalTree { private: const int size; const function &joinValue; const function &joinDelta; const function &accumulate; const function &initValue; vector value; vector delta; void init(int root, int left, int right) { if (left + 1 == right) { value[root] = initValue(left); } else { int mid = (left + right) >> 1; init(2 * root + 1, left, mid); init(2 * root + 2, mid, right); value[root] = joinValue(value[2 * root + 1], value[2 * root + 2]); } } void apply(int root, Delta dlt, int left, int right) { value[root] = accumulate(value[root], dlt, left, right); delta[root] = joinDelta(delta[root], dlt); } void pushDown(int root, int left, int mid, int right) { apply(2 * root + 1, delta[root], left, mid); apply(2 * root + 2, delta[root], mid, right); delta[root] = defaultDelta; } void update(int root, int left, int right, int from, int to, Delta dlt) { if (left >= from && right <= to) { apply(root, dlt, left, right); return; } if (right <= from || left >= to) { return; } int mid = (left + right) >> 1; pushDown(root, left, mid, right); update(2 * root + 1, left, mid, from, to, dlt); update(2 * root + 2, mid, right, from, to, dlt); } Value query(int root, int left, int right, int from, int to) { if (left >= from && right <= to) { return value[root]; } if (right <= from || left >= to) { return defaultValue; } int mid = (left + right) >> 1; pushDown(root, left, mid, right); return joinValue(query(2 * root + 1, left, mid, from, to), query(2 * root + 2, mid, right, from, to)); } public: IntervalTree(int size, const function &joinValue, const function &joinDelta, function &accumulate, const function &initValue = [](int at) -> Value { return defaultValue; }) : size(size), joinValue(joinValue), joinDelta(joinDelta), accumulate(accumulate), initValue(initValue) { int vertexSize = size * 4; value = vector(vertexSize); delta = vector(vertexSize, defaultDelta); init(0, 0, size); } void update(int from, int to, Delta delta) { update(0, 0, size, from, to, delta); } Value query(int from, int to) { return query(0, 0, size, from, to); } }; template class ReadOnlyIntervalTree { private: const int size; const function &joinValue; vector value; void init(int root, int left, int right, const vector& array) { if (left + 1 == right) { value[root] = array[left]; } else { int mid = (left + right) >> 1; init(2 * root + 1, left, mid, array); init(2 * root + 2, mid, right, array); value[root] = joinValue(value[2 * root + 1], value[2 * root + 2]); } } Value query(int root, int left, int right, int from, int to) { if (left >= from && right <= to) { return value[root]; } if (right <= from || left >= to) { return defaultValue; } int mid = (left + right) >> 1; return joinValue(query(2 * root + 1, left, mid, from, to), query(2 * root + 2, mid, right, from, to)); } public: ReadOnlyIntervalTree(const vector& array, const function &joinValue) : size(array.size()), joinValue(joinValue) { int vertexSize = size * 4; value = vector(vertexSize); init(0, 0, size, array); } Value query(int from, int to) { return query(0, 0, size, from, to); } }; #endif //JHELPER_EXAMPLE_PROJECT_INTERVAL_TREE_H template class LCA { private: vi order; vi position; Graph* graph; ReadOnlyIntervalTree* lcaTree; vi level; public: LCA(Graph* graph, int root = 0) : graph(graph) { int vertexCount = graph->vertexCount; order = vi(2 * vertexCount - 1); position = vi(vertexCount, -1); level = vi(vertexCount); vi index = vi(vertexCount); vi last = vi(vertexCount); vi stack = vi(vertexCount); stack[0] = root; int size = 1; int j = 0; last[root] = -1; while (size > 0) { int vertex = stack[--size]; if (position[vertex] == -1) { position[vertex] = j; } order[j++] = vertex; if (last[vertex] != -1) { level[vertex] = level[last[vertex]] + 1; } while (index[vertex] < graph->edges[vertex].size() && last[vertex] == graph->edges[vertex][index[vertex]]->to) { index[vertex]++; } if (index[vertex] < graph->edges[vertex].size()) { stack[size++] = vertex; int to = graph->edges[vertex][index[vertex]]->to; stack[size++] = graph->edges[vertex][index[vertex]]->to; last[to] = vertex; index[vertex]++; } } lcaTree = new ReadOnlyIntervalTree(order, [this](int left, int right) -> int { if (left == -1) { return right; } if (right == -1) { return left; } if (level[left] < level[right]) { return left; } return right; }); } int getPosition(int vertex) { return position[vertex]; } int getLCA(int first, int second) { return lcaTree->query(min(position[first], position[second]), max(position[first], position[second]) + 1); } int getLevel(int vertex) { return level[vertex]; } int getPathLength(int first, int second) { return level[first] + level[second] - 2 * level[getLCA(first, second)]; } }; #endif //CPP_LCA_H class c { int fillDown(int vertex, int last, Graph& graph, vi& qty, vi& part, vi& down) { down[vertex] = qty[part[vertex]]; for (auto edge : graph.edges[vertex]) { if (edge->to != last) { down[vertex] += fillDown(edge->to, vertex, graph, qty, part, down); } } return down[vertex]; } int getOptimal(int vertex, int last, Graph& graph, vi& down, int all) { for (auto edge : graph.edges[vertex]) { if (edge->to != last && down[edge->to] * 2 > all) { return getOptimal(edge->to, vertex, graph, down, all); } } return vertex; } public: void solve(istream& inp, ostream& outp) { Input in(inp); Output out(outp); int n = in.readInt(); int m = in.readInt(); int q = in.readInt(); vi u, v, a, b; tie(u, v) = in.readArrays(m); tie(a, b) = in.readArrays(q); decreaseByOne(u, v, a, b); DSU dsu(n); for (int i = 0; i < m; ++i) { dsu.join(u[i], v[i]); } int setCount = dsu.getSetCount(); vi heads; for (int i = 0; i < n; i++) { if (dsu.get(i) == i) { heads.push_back(i); } } vi did(n); for (int i = 0; i < n; i++) { did[i] = lower_bound(all(heads), dsu.get(i)) - heads.begin(); } vector parts(setCount); for (int i = 0; i < n; i++) { parts[did[i]].push_back(i); } vi size(setCount); for (int i = 0; i < n; i++) { size[did[i]]++; } vector*> graphs; graphs.reserve(setCount); for (int i = 0; i < setCount; i++) { graphs.push_back(new Graph(size[i])); } vi convert(n); for (int i = 0; i < n; i++) { int id = did[i]; convert[i] = lower_bound(all(parts[id]), i) - parts[id].begin(); } for (int i = 0; i < m; i++) { int id = did[v[i]]; graphs[id]->addEdge(new BidirectionalEdge(convert[v[i]], convert[u[i]])); } vector*> lcas; for (int i = 0; i < setCount; i++) { lcas.push_back(new LCA(graphs[i])); } ll answer = 0; vi qty(n); for (int i = 0; i < q; ++i) { if (did[a[i]] != did[b[i]]) { qty[a[i]]++; qty[b[i]]++; } else { answer += lcas[did[a[i]]]->getPathLength(convert[a[i]], convert[b[i]]); } } for (int i = 0; i < setCount; i++) { vi down(parts[i].size()); fillDown(0, -1, *graphs[i], qty, parts[i], down); int optimal = getOptimal(0, -1, *graphs[i], down, down[0]); for (int j = 0; j < parts[i].size(); j++) { answer += lcas[i]->getPathLength(j, optimal) * ll(qty[parts[i][j]]); } } out.printLine(answer); } }; int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); c solver; std::istream& in(std::cin); std::ostream& out(std::cout); solver.solve(in, out); return 0; }