#include using i64 = long long; using namespace std; template class MaximumWeightedMatching { /* Maximum Weighted Matching in General Graphs. - O(nm log(n)) time - O(n + m) space Note: each vertex is 1-indexed. */ public: using cost_t = CostType; using tcost_t = TotalCostType; private: enum Label { kSeparated = -2, kInner = -1, kFree = 0, kOuter = 1 }; static constexpr cost_t Inf = cost_t(1) << (sizeof(cost_t) * 8 - 2); private: template class BinaryHeap { public: struct Node { bool operator<(const Node& rhs) const { return value < rhs.value; } T value; int id; }; BinaryHeap() {} BinaryHeap(int N) : size_(0), node(N + 1), index(N, 0) {} int size() const { return size_; } bool empty() const { return size_ == 0; } void clear() { while (size_ > 0) index[node[size_--].id] = 0; } T min() const { return node[1].value; } int argmin() const { return node[1].id; } // argmin ? T get_val(int id) const { return node[index[id]].value; } void pop() { if (size_ > 0) pop(1); } void erase(int id) { if (index[id]) pop(index[id]); } bool has(int id) const { return index[id] != 0; } void update(int id, T v) { if (!has(id)) return push(id, v); bool up = (v < node[index[id]].value); node[index[id]].value = v; if (up) up_heap(index[id]); else down_heap(index[id]); } void decrease_key(int id, T v) { if (!has(id)) return push(id, v); if (v < node[index[id]].value) node[index[id]].value = v, up_heap(index[id]); } void push(int id, T v) { // assert(!has(id)); index[id] = ++size_; node[size_] = {v, id}; up_heap(size_); } private: void pop(int pos) { index[node[pos].id] = 0; if (pos == size_) { --size_; return; } bool up = (node[size_].value < node[pos].value); node[pos] = node[size_--]; index[node[pos].id] = pos; if (up) up_heap(pos); else down_heap(pos); } void swap_node(int a, int b) { swap(node[a], node[b]); index[node[a].id] = a; index[node[b].id] = b; } void down_heap(int pos) { for (int k = pos, nk = k; 2 * k <= size_; k = nk) { if (node[2 * k] < node[nk]) nk = 2 * k; if (2 * k + 1 <= size_ && node[2 * k + 1] < node[nk]) nk = 2 * k + 1; if (nk == k) break; swap_node(k, nk); } } void up_heap(int pos) { for (int k = pos; k > 1 && node[k] < node[k >> 1]; k >>= 1) swap_node(k, k >> 1); } int size_; vector node; vector index; }; template class PairingHeaps { private: struct Node { Node() : prev(-1) {} // "prev < 0" means the node is unused. Node(Key v) : key(v), child(0), next(0), prev(0) {} Key key; int child, next, prev; }; public: PairingHeaps(int H, int N) : heap(H), node(N) { // It consists of `H` Pairing heaps. // Each heap-node ID can appear at most 1 time(s) among heaps // and should be in [1, N). } void clear(int h) { if (heap[h]) clear_rec(heap[h]), heap[h] = 0; } void clear_all() { for (size_t i = 0; i < heap.size(); ++i) heap[i] = 0; for (size_t i = 0; i < node.size(); ++i) node[i] = Node(); } bool empty(int h) const { return !heap[h]; } bool used(int v) const { return node[v].prev >= 0; } Key min(int h) const { return node[heap[h]].key; } int argmin(int h) const { return heap[h]; } void pop(int h) { // assert(!empty(h)); erase(h, heap[h]); } void push(int h, int v, Key key) { // assert(!used(v)); node[v] = Node(key); heap[h] = merge(heap[h], v); } void erase(int h, int v) { if (!used(v)) return; int w = two_pass_pairing(node[v].child); if (!node[v].prev) heap[h] = w; else { cut(v); heap[h] = merge(heap[h], w); } node[v].prev = -1; } void decrease_key(int h, int v, Key key) { if (!used(v)) return push(h, v, key); if (!node[v].prev) node[v].key = key; else { cut(v); node[v].key = key; heap[h] = merge(heap[h], v); } } private: void clear_rec(int v) { for (; v; v = node[v].next) { if (node[v].child) clear_rec(node[v].child); node[v].prev = -1; } } inline void cut(int v) { auto& n = node[v]; int pv = n.prev, nv = n.next; auto& pn = node[pv]; if (pn.child == v) pn.child = nv; else pn.next = nv; node[nv].prev = pv; n.next = n.prev = 0; } int merge(int l, int r) { if (!l) return r; if (!r) return l; if (node[l].key > node[r].key) swap(l, r); int lc = node[r].next = node[l].child; node[l].child = node[lc].prev = r; return node[r].prev = l; } int two_pass_pairing(int root) { if (!root) return 0; int a = root; root = 0; while (a) { int b = node[a].next, na = 0; node[a].prev = node[a].next = 0; if (b) na = node[b].next, node[b].prev = node[b].next = 0; a = merge(a, b); node[a].next = root; root = a; a = na; } int s = node[root].next; node[root].next = 0; while (s) { int t = node[s].next; node[s].next = 0; root = merge(root, s); s = t; } return root; } private: vector heap; vector node; }; template struct PriorityQueue : public priority_queue, greater > { PriorityQueue() {} PriorityQueue(int N) { this->c.reserve(N); } T min() { return this->top(); } void clear() { this->c.clear(); } }; template struct Queue { Queue() {} Queue(int N) : qh(0), qt(0), data(N) {} T operator[](int i) const { return data[i]; } void enqueue(int u) { data[qt++] = u; } int dequeue() { return data[qh++]; } bool empty() const { return qh == qt; } void clear() { qh = qt = 0; } int size() const { return qt; } int qh, qt; vector data; }; public: struct InputEdge { int from, to; cost_t cost; }; private: template using ModifiableHeap = BinaryHeap; template using ModifiableHeaps = PairingHeaps; template using FastHeap = PriorityQueue; struct Edge { int to; cost_t cost; }; struct Link { int from, to; }; struct Node { struct NodeLink { int b, v; }; Node() {} Node(int u) : parent(0), size(1) { link[0] = link[1] = {u, u}; } int next_v() const { return link[0].v; } int next_b() const { return link[0].b; } int prev_v() const { return link[1].v; } int prev_b() const { return link[1].b; } int parent, size; NodeLink link[2]; }; struct Event { Event() {} Event(cost_t time, int id) : time(time), id(id) {} bool operator<(const Event& rhs) const { return time < rhs.time; } bool operator>(const Event& rhs) const { return time > rhs.time; } cost_t time; int id; }; struct EdgeEvent { EdgeEvent() {} EdgeEvent(cost_t time, int from, int to) : time(time), from(from), to(to) {} bool operator>(const EdgeEvent& rhs) const { return time > rhs.time; } bool operator<(const EdgeEvent& rhs) const { return time < rhs.time; } cost_t time; int from, to; }; public: MaximumWeightedMatching(int N, const vector& in) : N(N), B((N - 1) / 2), S(N + B + 1), ofs(N + 2), edges(in.size() * 2), heap2(S), heap2s(S, S), heap3(edges.size()), heap4(S) { for (auto& e : in) ofs[e.from + 1]++, ofs[e.to + 1]++; for (int i = 1; i <= N + 1; ++i) ofs[i] += ofs[i - 1]; for (auto& e : in) { edges[ofs[e.from]++] = {e.to, e.cost * 2}; edges[ofs[e.to]++] = {e.from, e.cost * 2}; } for (int i = N + 1; i > 0; --i) ofs[i] = ofs[i - 1]; ofs[0] = 0; } pair > maximum_weighted_matching(bool init_matching = false) { initialize(); set_potential(); if (init_matching) find_maximal_matching(); for (int u = 1; u <= N; ++u) if (!mate[u]) do_edmonds_search(u); tcost_t ret = compute_optimal_value(); return make_pair(ret, mate); } private: tcost_t compute_optimal_value() const { tcost_t ret = 0; for (int u = 1; u <= N; ++u) if (mate[u] > u) { cost_t max_c = 0; for (int eid = ofs[u]; eid < ofs[u + 1]; ++eid) { if (edges[eid].to == mate[u]) max_c = max(max_c, edges[eid].cost); } ret += max_c; } return ret >> 1; } inline tcost_t reduced_cost(int u, int v, const Edge& e) const { return tcost_t(potential[u]) + potential[v] - e.cost; } void rematch(int v, int w) { int t = mate[v]; mate[v] = w; if (mate[t] != v) return; if (link[v].to == surface[link[v].to]) { mate[t] = link[v].from; rematch(mate[t], t); } else { int x = link[v].from, y = link[v].to; rematch(x, y); rematch(y, x); } } void fix_mate_and_base(int b) { if (b <= N) return; int bv = base[b], mv = node[bv].link[0].v, bmv = node[bv].link[0].b; int d = (node[bmv].link[1].v == mate[mv]) ? 0 : 1; while (1) { int mv = node[bv].link[d].v, bmv = node[bv].link[d].b; if (node[bmv].link[1 ^ d].v != mate[mv]) break; fix_mate_and_base(bv); fix_mate_and_base(bmv); bv = node[bmv].link[d].b; } fix_mate_and_base(base[b] = bv); mate[b] = mate[bv]; } void reset_time() { time_current_ = 0; event1 = {Inf, 0}; } void reset_blossom(int b) { label[b] = kFree; link[b].from = 0; slack[b] = Inf; lazy[b] = 0; } void reset_all() { label[0] = kFree; link[0].from = 0; for (int v = 1; v <= N; ++v) { // should be optimized for sparse graphs. if (label[v] == kOuter) potential[v] -= time_current_; else { int bv = surface[v]; potential[v] += lazy[bv]; if (label[bv] == kInner) potential[v] += time_current_ - time_created[bv]; } reset_blossom(v); } for (int b = N + 1, r = B - unused_bid_idx_; r > 0 && b < S; ++b) if (base[b] != b) { if (surface[b] == b) { fix_mate_and_base(b); if (label[b] == kOuter) potential[b] += (time_current_ - time_created[b]) << 1; else if (label[b] == kInner) fix_blossom_potential(b); else fix_blossom_potential(b); } heap2s.clear(b); reset_blossom(b); --r; } que.clear(); reset_time(); heap2.clear(); heap3.clear(); heap4.clear(); } void do_edmonds_search(int root) { if (potential[root] == 0) return; link_blossom(surface[root], {0, 0}); push_outer_and_fix_potentials(surface[root], 0); for (bool augmented = false; !augmented;) { augmented = augment(root); if (augmented) break; augmented = adjust_dual_variables(root); } reset_all(); } template