CodeForces Gym 102156 简要题解

Takeover

每次贪心选增量最小的。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<int> x(n), y(n);
  for (int i = 0; i < n; ++i) {
    cin >> x[i] >> y[i];
  }
  vector<int> by_x(n), by_y(n), by_z(n);
  for (int i = 0; i < n; ++i) {
    by_x[i] = by_y[i] = by_z[i] = i;
  }
  sort(by_x.begin(), by_x.end(), [&](int a, int b) {
    return x[a] < x[b];
  });
  sort(by_y.begin(), by_y.end(), [&](int a, int b) {
    return y[a] < y[b];
  });
  sort(by_z.begin(), by_z.end(), [&](int a, int b) {
    return x[a] + y[a] < x[b] + y[b];
  });
  int ptr_x = 0, ptr_y = 0, ptr_z = 0;
  int cur_x = 1, cur_y = 1;
  priority_queue<pair<int, int>> q_x, q_y;
  vector<bool> done(n);
  for (int rep = 0; rep < n; ++rep) {
    while (ptr_x < n && x[by_x[ptr_x]] <= cur_x) {
      q_y.emplace(-y[by_x[ptr_x]], by_x[ptr_x]);
      ++ptr_x;
    }
    while (ptr_y < n && y[by_y[ptr_y]] <= cur_y) {
      q_x.emplace(-x[by_y[ptr_y]], by_y[ptr_y]);
      ++ptr_y;
    }
    while (ptr_z < n && (done[by_z[ptr_z]] || x[by_z[ptr_z]] <= cur_x || y[by_z[ptr_z]] <= cur_y)) {
      ++ptr_z;
    }
    while (!q_x.empty() && done[q_x.top().second]) {
      q_x.pop();
    }
    while (!q_y.empty() && done[q_y.top().second]) {
      q_y.pop();
    }
    pair<int, int> choice = make_pair(INT_MAX, -1);
    if (!q_x.empty()) {
      choice = min(choice, make_pair(max(0, -(q_x.top().first + cur_x)), q_x.top().second));
    }
    if (!q_y.empty()) {
      choice = min(choice, make_pair(max(0, -(q_y.top().first + cur_y)), q_y.top().second));
    }
    if (ptr_z < n) {
      choice = min(choice, make_pair(x[by_z[ptr_z]] + y[by_z[ptr_z]] - cur_x - cur_y, by_z[ptr_z]));
    }
    int id = choice.second;
    done[id] = true;
    cur_x = max(cur_x, x[id]);
    cur_y = max(cur_y, y[id]);
    if (rep) {
      cout << " ";
    }
    cout << id + 1;
  }
  cout << "\n";
  return 0;
}

Unfair Card Deck

f ( i , j ) f(i,j) f(i,j) 表示 i i i j j j 前出现次数,则 f ( i , j ) / f ( j , i ) f(i,j)/f(j,i) f(i,j)/f(j,i) 可以近似得到 w i w_i wi w j w_j wj 的比值。考虑每次选出 w w w 最大的,用所有已有的卡片来计算平均值。需要注意的是,如果 f ( i , j ) / f ( j , i ) < 1 0 − 3 f(i,j)/f(j,i)<10^{-3} f(i,j)/f(j,i)<103 ,这个卡片是相对没有代表性的,它不能很好反映概率,应该丢掉。如果所有已经确定卡片都在当前卡片前面出现,则它们都没有代表性,需要用之前最小的 w w w 乘上 1 0 − 7 10^{-7} 107 (极大概率出现在他们后面,同时也能区分后面的卡片)。

#include 

using namespace std;

typedef long double ld;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout.setf(ios::fixed);
  cout.precision(290);
  int m, n;
  cin >> m >> n;
  int cards = 0;
  for (int i = 0; i < n; ++i) {
    int x;
    cin >> x;
    cards += x;
  }
  vector<vector<int>> before(n, vector<int>(n));
  for (int i = 0; i < m; ++i) {
    vector<int> log(cards);
    for (int j = 0; j < cards; ++j) {
      cin >> log[j];
      --log[j];
    }
    for (int j = 0; j < cards; ++j) {
      for (int k = j + 1; k < cards; ++k) {
        if (log[j] != log[k]) {
          ++before[log[j]][log[k]];
        }
      }
    }
  }
  vector<bool> used(n);
  vector<ld> ans(n);
  int first = 0;
  for (int i = 1; i < n; ++i) {
    if (before[i][first] > before[first][i]) {
      first = i;
    }
  }
  used[first] = true;
  ans[first] = 1;
  for (int rep = 1; rep < n; ++rep) {
    vector<ld> prob(n);
    for (int i = 0; i < n; ++i) {
      if (!used[i]) {
        vector<int> all;
        for (int j = 0; j < n; ++j) {
          if (used[j]) {
            all.push_back(j);
          }
        }
        sort(all.begin(), all.end(), [&](int a, int b) {
          return ans[a] < ans[b];
        });
        while ((int) all.size() > 1 && before[i][all.back()] < before[all.back()][i] * 1e-3) {
          all.pop_back();
        }
        ld ave = 0;
        for (auto j : all) {
          ave += before[i][j] * ans[j] / before[j][i];
        }
        ave /= all.size();
        if (ave < 1e-300) {
          prob[i] = 1;
          for (int j = 0; j < n; ++j) {
            if (used[j]) {
              prob[i] = min(prob[i], ans[j]);
            }
          }
          prob[i] *= 1e-7;
        } else {
          prob[i] = ave;
        }
      }
    }
    int p = -1;
    for (int i = 0; i < n; ++i) {
      if (!used[i] && (p == -1 || prob[i] > prob[p] || (prob[i] == prob[p] && before[i][p] > before[p][i]))) {
        p = i;
      }
    }
    ans[p] = prob[p];
    used[p] = true;
  }
  for (int i = 0; i < n; ++i) {
    if (ans[i] < 1e-233) {
      ans[i] = 1e-233;
    }
    if (ans[i] > 1) {
      ans[i] = 1;
    }
    cout << ans[i] << "\n";
  }
  return 0;
}

Diverse Singing

对于每个点每种颜色限流,跑上下界网络流即可。

#include 

using namespace std;

using cap_t = int;

const int cap_inf = 0x3f3f3f3f;

namespace flow {
struct edge {
  int to, rev;
  cap_t cap;
  
  edge(int t, cap_t c, int r) {
    to = t;
    cap = c;
    rev = r;
  }
};

vector<vector<edge>> adj;
vector<int> cur, dist;
int n, source, sink;
cap_t ans;

void init(int v, int s, int t) {
  n = v;
  ans = 0;
  source = s;
  sink = t;
  adj.clear();
  adj.resize(n);
  cur.resize(n);
  dist.resize(n);
}

void add(int x, int y, cap_t c) {
  adj[x].emplace_back(y, c, adj[y].size());
  adj[y].emplace_back(x, 0, adj[x].size() - 1);
}

bool bfs() {
  queue<int> q;
  for (int i = 0; i < n; ++i) {
    dist[i] = -1;
  }
  dist[source] = 0;
  q.push(source);
  while (!q.empty()) {
    int x = q.front();
    q.pop();
    for (auto e : adj[x]) {
      if (e.cap && !~dist[e.to]) {
        dist[e.to] = dist[x] + 1;
        if (e.to == sink) {
          return true;
        }
        q.push(e.to);
      }
    }
  }
  return false;
}

cap_t dfs(int x, cap_t f) {
  if (x == sink) {
    return f;
  }
  for (int &i = cur[x]; ~i; --i) {
    edge &e = adj[x][i];
    if (e.cap && dist[e.to] == dist[x] + 1) {
      cap_t w = dfs(e.to, min(e.cap, f));
      if (w) {
        e.cap -= w;
        adj[e.to][e.rev].cap += w;
        return w;
      }
    }
  }
  return 0;
}

cap_t max_flow() {
  while (bfs()) {
    for (int i = 0; i < n; ++i) {
      cur[i] = adj[i].size() - 1;
    }
    while (true) {
      cap_t flow = dfs(source, cap_inf);
      if (!flow) {
        break;
      }
      ans += flow;
    }
  }
  return ans;
}
}

using flow::init;
using flow::add;
using flow::max_flow;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m, e;
  cin >> n >> m >> e;
  vector<int> from(e), to(e), color(e);
  vector<map<int, int>> left_id(n);
  vector<map<int, int>> right_id(m);
  int cnt = n + m;
  for (int i = 0; i < e; ++i) {
    cin >> from[i] >> to[i] >> color[i];
    --from[i];
    --to[i];
    if (!left_id[from[i]].count(color[i])) {
      left_id[from[i]][color[i]] = cnt++;
    }
    if (!right_id[to[i]].count(color[i])) {
      right_id[to[i]][color[i]] = cnt++;
    }
  }
  init(cnt + 4, cnt + 2, cnt + 3);
  for (int i = 0; i < e; ++i) {
    add(left_id[from[i]][color[i]], right_id[to[i]][color[i]], 1);
  }
  for (int i = 0; i < n; ++i) {
    add(cnt + 0, i, cap_inf);
    add(cnt + 2, i, 1);
    for (auto p : left_id[i]) {
      add(i, p.second, 1);
    }
  }
  for (int i = 0; i < m; ++i) {
    add(i + n, cnt + 1, cap_inf);
    add(i + n, cnt + 3, 1);
    for (auto p : right_id[i]) {
      add(p.second, i + n, 1);
    }
  }
  add(cnt + 0, cnt + 3, n);
  add(cnt + 2, cnt + 1, m);
  add(cnt + 1, cnt + 0, cap_inf);
  if (max_flow() != n + m) {
    cout << -1 << "\n";
    return 0;
  }
  set<pair<int, int>> st;
  for (int i = 0; i < n; ++i) {
    for (auto p : left_id[i]) {
      for (auto e : flow::adj[p.second]) {
        if (e.to >= n + m && !e.cap) {
          st.insert(make_pair(p.second, e.to));
        }
      }
    }
  }
  vector<int> ans;
  for (int i = 0; i < e; ++i) {
    if (st.count(make_pair(left_id[from[i]][color[i]], right_id[to[i]][color[i]]))) {
      ans.push_back(i);
      st.erase(make_pair(left_id[from[i]][color[i]], right_id[to[i]][color[i]]));
    }
  }
  cout << ans.size() << "\n";
  for (int i = 0; i < (int) ans.size(); ++i) {
    if (i) {
      cout << " ";
    }
    cout << ans[i] + 1;
  }
  cout << "\n";
  return 0;
}

Pick Your Own Nim

拟阵交。第一个拟阵是要求线性无关,第二个拟阵是每堆最多选一个。

#include 

using namespace std;

const int LOG = 60;

struct base {
  vector<long long> b;

  base() {
    b.assign(LOG, 0);
  }

  void clear() {
    for (int i = 0; i < LOG; ++i) {
      b[i] = 0;
    }
  }

  void append(long long x) {
    for (int i = 0; i < LOG; ++i) {
      if (x >> i & 1) {
        if (b[i]) {
          x ^= b[i];
        } else {
          b[i] = x;
          return;
        }
      }
    }
  }

  bool check(long long x) {
    for (int i = 0; i < LOG; ++i) {
      if (x >> i & 1) {
        if (b[i]) {
          x ^= b[i];
        } else {
          return true;
        }
      }
    }
    return false;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<long long> values;
  vector<int> colors;
  for (int i = 0; i < n; ++i) {
    long long x;
    cin >> x;
    values.push_back(x);
    colors.push_back(i);
  }
  int m;
  cin >> m;
  for (int i = 0; i < m; ++i) {
    int sz;
    cin >> sz;
    while (sz--) {
      long long x;
      cin >> x;
      values.push_back(x);
      colors.push_back(i + n);
    }
  }
  int s = values.size();
  vector<base> without(s);
  base everything;
  set<int> st;
  auto solve = [&]() {
    vector<bool> used(n + m);
    everything.clear();
    for (auto x : st) {
      used[colors[x]] = true;
      everything.append(values[x]);
      without[x].clear();
      for (auto y : st) {
        if (y != x) {
          without[x].append(values[y]);
        }
      }
    }
    vector<int> pre(s, -1);
    queue<int> q;
    for (int i = 0; i < s; ++i) {
      if (!st.count(i)) {
        if (everything.check(values[i])) {
          if (!used[colors[i]]) {
            st.insert(i);
            return;
          } else {
            pre[i] = -2;
            q.push(i);
          }
        }
      }
    }
    auto check = [&](int i, int j) {
      if (st.count(i)) {
        return without[i].check(values[j]);
      } else {
        return colors[i] == colors[j] || !used[colors[i]];
      }
    };
    while (!q.empty()) {
      int i = q.front();
      q.pop();
      for (int j = 0; j < s; ++j) {
        if (pre[j] == -1 && st.count(i) != st.count(j) && check(i, j)) {
          pre[j] = i;
          if (!used[colors[j]]) {
            for (int k = j; k != -2; k = pre[k]) {
              if (st.count(k)) {
                st.erase(k);
              } else {
                st.insert(k);
              }
            }
            return;
          } else {
            q.push(j);
          }
        }
      }
    }
    cout << -1 << "\n";
    exit(0);
  };
  for (int i = 0; i < n + m; ++i) {
    solve();
  }
  vector<long long> ans(n + m);
  for (auto x : st) {
    ans[colors[x]] = values[x];
  }
  for (int i = n; i < n + m; ++i) {
    cout << ans[i] << "\n";
  }
  return 0;
}

Permutasino

为了方便,我们将 x i x_i xi 排序,则和必须要对,且需要满足 ∑ j = 1 i x j ≥ ∑ j = 1 i j \sum_{j=1}^i x_j\ge \sum_{j=1}^i j j=1ixjj=1ij ,否则无解。其余情况都是有解的。

用归纳法证明这个结论。考虑用 ( 1 , 2 , ⋯   , n ) , ( y 1 , y 2 , ⋯   , y n ) (1, 2, \cdots, n), (y_1, y_2, \cdots, y_n) (1,2,,n),(y1,y2,,yn) 来线性表示 ( x 1 , x 2 , ⋯   , x n ) (x_1, x_2, \cdots, x_n) (x1,x2,,xn) ,则 y y y 可以用一个 r a t i o ratio ratio 来表示。二分求出最大的 r a t i o ratio ratio ,使得 y y y 仍然满足排序后前缀和大于排列前缀和,此时 y y y 存在一个位置可以分成两半(排序后前缀和等于排列前缀和),两部分递归处理。考虑合并,用类似归并排序的方法可以将一个 n n n 的子问题和一个 m m m 的子问题用 n + m − 1 n+m-1 n+m1 个排列凑出来,加上 ( 1 , 2 , ⋯   , n + m ) (1, 2, \cdots, n+m) (1,2,,n+m) 恰好 n + m n+m n+m 个。

这里需要注意一些精度问题,例如二分后找到限制最紧的来计算 r a t i o ratio ratio ;以及可以用一个随机排列而不是 ( 1 , 2 , ⋯   , n ) (1, 2, \cdots, n) (1,2,,n) 作为一组解。我写了一个怎么调eps都过不去,就抄了一遍std。

#include 

using namespace std;

typedef long double ld;

const ld eps = 1e-9;

vector<int> last;

bool check(vector<ld> a) {
  int n = (int) a.size();
  vector<pair<ld, int>> sorted(n);
  for (int i = 0; i < n; ++i) {
    sorted[i] = make_pair(a[i], i);
  }
  sort(sorted.begin(), sorted.end());
  ld pref = 0, need = 0;
  for (int i = 0; i < n; ++i) {
    pref += sorted[i].first;
    need += i;
    if (pref < need - eps) {
      last.clear();
      for (int j = 0; j <= i; ++j) {
        last.push_back(sorted[j].second);
      }
      return false;
    }
  }
  return true;
}

vector<pair<ld, vector<int>>> solve(vector<ld> a) {
  int n = (int) a.size();
  vector<pair<ld, int>> sorted(n);
  for (int i = 0; i < n; ++i) {
    sorted[i] = make_pair(a[i], i);
  }
  sort(sorted.begin(), sorted.end());
  for (int i = 0; i < n; ++i) {
    a[i] = sorted[i].first;
  }
  bool is_perm = true;
  for (int i = 0; i < n; ++i) {
    if (fabs(a[i] - i) > eps) {
      is_perm = false;
    }
  }
  vector<pair<ld, vector<int>>> ans;
  if (is_perm) {
    ans.emplace_back(1, vector<int>());
    for (int i = 0; i < n; ++i) {
      ans.back().second.push_back((int) (a[i] + 0.5));
    }
  } else {
    vector<ld> init(n);
    for (int i = 0; i < n; ++i) {
      init[i] = i;
    }
    random_shuffle(init.begin(), init.end());
    vector<ld> dir(n);
    ld max_dir = 0;
    for (int i = 0; i < n; ++i) {
      dir[i] = a[i] - init[i];
      max_dir = max(max_dir, fabs(dir[i]));
    }
    for (int i = 0; i < n; ++i) {
      dir[i] /= max_dir;
    }
    ld l = 0, r = -1;
    for (int i = 0; i < n; ++i) {
      if (dir[i] < 0) {
        ld t = (init[i] + 1) / -dir[i];
        if (r > t || r < -0.5) {
          r = t;
        }
      }
    }
    last.clear();
    for (int rep = 0; rep < 100; ++rep) {
      ld mid = (l + r) / 2;
      vector<ld> p(n);
      for (int i = 0; i < n; ++i) {
        p[i] = init[i] + dir[i] * mid;
      }
      if (check(p)) {
        l = mid;
      } else {
        r = mid;
      }
    }
    {
      ld pref = 0, need = 0, speed = 0;
      for (int i = 0; i < (int) last.size(); ++i) {
        pref += init[last[i]];
        need += i;
        speed += dir[last[i]];
      }
      l = (need - pref) / speed;
    }
    vector<ld> b(n);
    for (int i = 0; i < n; ++i) {
      b[i] = init[i] + dir[i] * l;
    }
    vector<pair<ld, int>> sorted(n);
    for (int i = 0; i < n; ++i) {
      sorted[i] = make_pair(b[i], i);
    }
    sort(sorted.begin(), sorted.end());
    for (int i = 0; i < n; ++i) {
      b[i] = sorted[i].first;
    }
    ld pref = 0, need = 0;
    int divide = n;
    for (int i = 0; i < n; ++i) {
      pref += b[i];
      need += i;
      if (pref <= need + eps) {
        divide = i + 1;
        break;
      }
    }
    for (int i = divide; i < n; ++i) {
      b[i] -= divide;
    }
    vector<pair<ld, vector<int>>> left = solve(vector<ld>(b.begin(), b.begin() + divide));
    vector<pair<ld, vector<int>>> right = solve(vector<ld>(b.begin() + divide, b.end()));
    int left_ptr = 0, right_ptr = 0;
    ld left_used = 0, right_used = 0;
    while (left_ptr < (int) left.size() && right_ptr < (int) right.size()) {
      ans.emplace_back(0, vector<int>());
      for (auto i : left[left_ptr].second) {
        ans.back().second.push_back(i);
      }
      for (auto i : right[right_ptr].second) {
        ans.back().second.push_back(i + divide);
      }
      if (left[left_ptr].first - left_used < right[right_ptr].first - right_used) {
        ans.back().first = left[left_ptr].first - left_used;
        right_used += ans.back().first;
        left_used = 0;
        ++left_ptr;
      } else {
        ans.back().first = right[right_ptr].first - right_used;
        left_used += ans.back().first;
        right_used = 0;
        ++right_ptr;
      }
    }
    for (auto &p : ans) {
      vector<int> new_p(n);
      for (int i = 0; i < n; ++i) {
        new_p[sorted[i].second] = p.second[i];
      }
      swap(p.second, new_p);
      p.first *= max_dir / l;
    }
    ans.emplace_back((l - max_dir) / l, vector<int>());
    for (int i = 0; i < n; ++i) {
      ans.back().second.push_back((int) (init[i] + 0.5));
    }
  }
  for (auto &p : ans) {
    vector<int> new_p(n);
    for (int i = 0; i < n; ++i) {
      new_p[sorted[i].second] = p.second[i];
    }
    swap(p.second, new_p);
  }
  return ans;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout.setf(ios::fixed);
  cout.precision(20);
  int n;
  cin >> n;
  vector<ld> a(n);
  ld pref = 0, need = 0;
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
    --a[i];
    pref += a[i];
    need += i;
  }
  if (fabs(pref - need) > eps || !check(a)) {
    cout << -1 << "\n";
    return 0;
  }
  vector<pair<ld, vector<int>>> ans = solve(a);
  vector<pair<ld, vector<int>>> new_ans;
  for (auto p : ans) {
    if (p.first > 1e-10) {
      new_ans.push_back(p);
    }
  }
  swap(ans, new_ans);
  cout << ans.size() << "\n";
  for (auto p : ans) {
    cout << p.first;
    for (auto x : p.second) {
      cout << " " << x + 1;
    }
    cout << "\n";
  }
  return 0;
}

Planar Max Cut

google这个标题你就会做了 考虑属于割集的边,不难发现需要形成一个二分图;而平面图是二分图当且仅当每个区域的环长是偶数。考虑删去一条边,会合并两个区域,要让图中没有奇环,只有两个有奇环的区域合并是有效的,偶环起过渡作用。求出对偶图中度数为奇数的任意两点最短路,然后求一般图最大权匹配即可。

#include 

using namespace std;

template<typename CostType, typename TotalCostType = int64_t>
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<typename T>
  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> node;
    vector<int> index;
  };

  template<typename Key>
  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<int> heap;
    vector<Node> node;
  };

  template<typename T>
  struct PriorityQueue : public priority_queue<T, vector<T>, greater<T>> {
    PriorityQueue() {}
    PriorityQueue(int N) { this->c.reserve(N);}
    T min() { return this->top(); }
    void clear() { this->c.clear(); }
  };

  template<typename T>
  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<T> data;
  };

 public:
  struct InputEdge { int from, to; cost_t cost; };

 private:
  template<typename T> using ModifiableHeap = BinaryHeap<T>;
  template<typename T> using ModifiableHeaps = PairingHeaps<T>;
  template<typename T> using FastHeap = PriorityQueue<T>;

  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<InputEdge> &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<tcost_t, vector<int>> 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<kInner>(b);
        else fix_blossom_potential<kFree>(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<Label Lab>
  inline cost_t fix_blossom_potential(int b) {
    // Return the amount.
    // (If v is an atom, the potential[v] will not be changed.)
    cost_t d = lazy[b]; lazy[b] = 0;
    if (Lab == kInner) {
      cost_t dt = time_current_ - time_created[b];
      if (b > N) potential[b] -= dt << 1;
      d += dt;
    }
    return d;
  }

  template<Label Lab>
  inline void update_heap2(int x, int y, int by, cost_t t) {
    if (t >= slack[y]) return;
    slack[y] = t; best_from[y] = x;
    if (y == by) {
      if (Lab != kInner) heap2.decrease_key(y, EdgeEvent(t + lazy[y], x, y));
    } else {
      int gy = group[y];
      if (gy != y) {
        if (t >= slack[gy]) return;
        slack[gy] = t;
      }
      heap2s.decrease_key(by, gy, EdgeEvent(t, x, y));
      if (Lab == kInner) return;
      EdgeEvent m = heap2s.min(by);
      heap2.decrease_key(by, EdgeEvent(m.time + lazy[by], m.from, m.to));
    }
  }

  void activate_heap2_node(int b) {
    if (b <= N) {
      if (slack[b] < Inf) heap2.push(b, EdgeEvent(slack[b] + lazy[b], best_from[b], b));
    } else {
      if (heap2s.empty(b)) return;
      EdgeEvent m = heap2s.min(b);
      heap2.push(b, EdgeEvent(m.time + lazy[b], m.from, m.to));
    }
  }

  void swap_blossom(int a, int b) {
    // Assume that `b` is a maximal blossom.
    swap(base[a], base[b]); if (base[a] == a) base[a] = b;
    swap(heavy[a], heavy[b]); if (heavy[a] == a) heavy[a] = b;
    swap(link[a], link[b]);
    swap(mate[a], mate[b]);
    swap(potential[a], potential[b]); swap(lazy[a], lazy[b]);
    swap(time_created[a], time_created[b]);
    for (int d = 0; d < 2; ++d) node[node[a].link[d].b].link[1 ^ d].b = b;
    swap(node[a], node[b]);
  }

  void set_surface_and_group(int b, int sf, int g) {
    surface[b] = sf, group[b] = g;
    if (b <= N) return;
    for (int bb = base[b]; surface[bb] != sf; bb = node[bb].next_b()) {
      set_surface_and_group(bb, sf, g);
    }
  }

  void merge_smaller_blossoms(int bid) {
    int lb = bid, largest_size = 1;
    for (int beta = base[bid], b = beta; ;) {
      if (node[b].size > largest_size) largest_size = node[b].size, lb = b;
      if ((b = node[b].next_b()) == beta) break;
    }
    for (int beta = base[bid], b = beta; ;) {
      if (b != lb) set_surface_and_group(b, lb, b);
      if ((b = node[b].next_b()) == beta) break;
    }
    group[lb] = lb;
    if (largest_size > 1) {
      surface[bid] = heavy[bid] = lb; 
      swap_blossom(lb, bid);
    } else heavy[bid] = 0;
  }

  void contract(int x, int y, int eid) {
    int bx = surface[x], by = surface[y]; assert(bx != by);
    const int h = -(eid + 1);
    link[surface[mate[bx]]].from = link[surface[mate[by]]].from = h;

    int lca = -1;
    while (1) {
      if (mate[by] != 0) swap(bx, by);
      bx = lca = surface[link[bx].from];
      if (link[surface[mate[bx]]].from == h) break;
      link[surface[mate[bx]]].from = h;
    }

    const int bid = unused_bid[--unused_bid_idx_]; assert(unused_bid_idx_ >= 0);
    int tree_size = 0;
    for (int d = 0; d < 2; ++d) {
      for (int bv = surface[x]; bv != lca; ) {
        int mv = mate[bv], bmv = surface[mv], v = mate[mv];
        int f = link[v].from, t = link[v].to;
        tree_size += node[bv].size + node[bmv].size;
        link[mv] = {x, y};

        if (bv > N) potential[bv] += (time_current_ - time_created[bv]) << 1;
        if (bmv > N) heap4.erase(bmv);
        push_outer_and_fix_potentials(bmv, fix_blossom_potential<kInner>(bmv));

        node[bv].link[d] = {bmv, mv};
        node[bmv].link[1 ^ d] = {bv, v}; node[bmv].link[d] = {bv = surface[f], f};
        node[bv].link[1 ^ d] = {bmv, t};
      }
      node[surface[x]].link[1 ^ d] = {surface[y], y};
      swap(x, y);
    }
    if (lca > N) potential[lca] += (time_current_ - time_created[lca]) << 1;
    node[bid].size = tree_size + node[lca].size;
    base[bid] = lca; link[bid] = link[lca]; mate[bid] = mate[lca];
    label[bid] = kOuter;
    surface[bid] = bid; time_created[bid] = time_current_;
    potential[bid] = 0; lazy[bid] = 0;

    merge_smaller_blossoms(bid); // O(n log n) time / Edmonds search
  }

  void link_blossom(int v, Link l) {
    link[v] = {l.from, l.to};
    if (v <= N) return;
    int b = base[v]; link_blossom(b, l);
    int pb = node[b].prev_b();
    l = {node[pb].next_v(), node[b].prev_v()};
    for (int bv = b; ; ) {
      int bw = node[bv].next_b();
      if (bw == b) break;
      link_blossom(bw, l);
      Link nl = {node[bw].prev_v(), node[bv].next_v()};
      bv = node[bw].next_b();
      link_blossom(bv, nl);
    }
  }

  void push_outer_and_fix_potentials(int v, cost_t d) {
    label[v] = kOuter;
    if (v > N) {
      for (int b = base[v]; label[b] != kOuter; b = node[b].next_b()) {
        push_outer_and_fix_potentials(b, d);
      }
    } else {
      potential[v] += time_current_ + d;
      if (potential[v] < event1.time) event1 = {potential[v], v};
      que.enqueue(v);
    }
  }

  bool grow(int root, int x, int y) {
    int by = surface[y];
    bool visited = (label[by] != kFree);
    if (!visited) link_blossom(by, {0, 0});
    label[by] = kInner; time_created[by] = time_current_; heap2.erase(by);
    if (y != by) heap4.update(by, time_current_ + (potential[by] >> 1));
    int z = mate[by];
    if (z == 0 && by != surface[root]) {
      rematch(x, y); rematch(y, x);
      return true;
    }
    int bz = surface[z];
    if (!visited) link_blossom(bz, {x, y}); 
    else link[bz] = link[z] = {x, y};
    push_outer_and_fix_potentials(bz, fix_blossom_potential<kFree>(bz));
    time_created[bz] = time_current_; heap2.erase(bz);
    return false;
  }

  void free_blossom(int bid) {
    unused_bid[unused_bid_idx_++] = bid;
    base[bid] = bid;
  }

  int recalculate_minimum_slack(int b, int g) {
    // Return the destination of the best edge of blossom `g`.
    if (b <= N) {
      if (slack[b] >= slack[g]) return 0;
      slack[g] = slack[b]; best_from[g] = best_from[b];
      return b;
    }
    int v = 0;
    for (int beta = base[b], bb = beta; ; ) {
      int w = recalculate_minimum_slack(bb, g);
      if (w != 0) v = w;
      if ((bb = node[bb].next_b()) == beta) break;
    }
    return v;
  }

  void construct_smaller_components(int b, int sf, int g) {
    surface[b] = sf, group[b] = g; // `group[b] = g` is unneeded.
    if (b <= N) return;
    for (int bb = base[b]; surface[bb] != sf; bb = node[bb].next_b()) {
      if (bb == heavy[b]) {
        construct_smaller_components(bb, sf, g);
      } else {
        set_surface_and_group(bb, sf, bb);
        int to = 0;
        if (bb > N) slack[bb] = Inf, to = recalculate_minimum_slack(bb, bb);
        else if (slack[bb] < Inf) to = bb;
        if (to > 0) heap2s.push(sf, bb, EdgeEvent(slack[bb], best_from[bb], to));
      }
    }
  }

  void move_to_largest_blossom(int bid) {
    const int h = heavy[bid];
    cost_t d = (time_current_ - time_created[bid]) + lazy[bid]; lazy[bid] = 0;
    for (int beta = base[bid], b = beta; ;) {
      time_created[b] = time_current_;
      lazy[b] = d;
      if (b != h) construct_smaller_components(b, b, b), heap2s.erase(bid, b);
      if ((b = node[b].next_b()) == beta) break;
    }
    if (h > 0) swap_blossom(h, bid), bid = h;
    free_blossom(bid);
  }

  void expand(int bid) {
    int mv = mate[base[bid]];
    move_to_largest_blossom(bid); // O(n log n) time / Edmonds search
    Link old_link = link[mv];
    int old_base = surface[mate[mv]], root = surface[old_link.to];
    int d = (mate[root] == node[root].link[0].v) ? 1 : 0;
    for (int b = node[old_base].link[d ^ 1].b; b != root; ) {
      label[b] = kSeparated; activate_heap2_node(b); b = node[b].link[d ^ 1].b;
      label[b] = kSeparated; activate_heap2_node(b); b = node[b].link[d ^ 1].b;
    }
    for (int b = old_base; ; b = node[b].link[d].b) {
      label[b] = kInner;
      int nb = node[b].link[d].b;
      if (b == root) link[mate[b]] = old_link;
      else link[mate[b]] = {node[b].link[d].v, node[nb].link[d ^ 1].v};
      link[surface[mate[b]]] = link[mate[b]]; // fix tree links
      if (b > N) {
        if (potential[b] == 0) expand(b);
        else heap4.push(b, time_current_ + (potential[b] >> 1));
      }
      if (b == root) break;
      push_outer_and_fix_potentials(nb, fix_blossom_potential<kInner>(b = nb));
    }
  }

  bool augment(int root) {
    // Return true if an augmenting path is found.
    while (!que.empty()) {
      int x = que.dequeue(), bx = surface[x];
      if (potential[x] == time_current_) {
        if (x != root) rematch(x, 0);
        return true;
      }
      for (int eid = ofs[x]; eid < ofs[x + 1]; ++eid) {
        auto &e = edges[eid]; int y = e.to, by = surface[y];
        if (bx == by) continue;
        Label l = label[by];
        if (l == kOuter) {
          cost_t t = reduced_cost(x, y, e) >> 1; // < 2 * Inf
          if (t == time_current_) {
            contract(x, y, eid); bx = surface[x];
          } else if (t < event1.time) {
            heap3.emplace(t, x, eid);
          }
        } else {
          tcost_t t = reduced_cost(x, y, e); // < 3 * Inf
          if (t >= Inf) continue;
          if (l != kInner) {
            if (cost_t(t) + lazy[by] == time_current_) {
              if (grow(root, x, y)) return true;
            } else update_heap2<kFree>(x, y, by, t);
          } else {
            if (mate[x] != y) update_heap2<kInner>(x, y, by, t);
          } 
        }
      }
    }
    return false;
  }

  bool adjust_dual_variables(int root) {
    // delta1 : rematch
    cost_t time1 = event1.time;

    // delta2 : grow
    cost_t time2 = Inf;
    if (!heap2.empty()) time2 = heap2.min().time;

    // delta3 : contract : O(m log n) time / Edmonds search [ bottleneck (?) ]
    cost_t time3 = Inf;
    while (!heap3.empty()) {
      EdgeEvent e = heap3.min();
      int x = e.from, y = edges[e.to].to; // e.to is some edge id.
      if (surface[x] != surface[y]) {
        time3 = e.time;
        break;
      } else heap3.pop();
    }

    // delta4 : expand
    cost_t time4 = Inf;
    if (!heap4.empty()) time4 = heap4.min();

    // -- events --
    cost_t time_next = min(min(time1, time2), min(time3, time4));
    assert(time_current_ <= time_next && time_next < Inf);
    time_current_ = time_next;

    if (time_current_ == event1.time) {
      int x = event1.id;
      if (x != root) rematch(x, 0);
      return true;
    }
    while (!heap2.empty() && heap2.min().time == time_current_) {
      int x = heap2.min().from, y = heap2.min().to;
      if (grow(root, x, y)) return true; // `grow` function will call `heap2.erase(by)`.
    }
    while (!heap3.empty() && heap3.min().time == time_current_) {
      int x = heap3.min().from, eid = heap3.min().to;
      int y = edges[eid].to; heap3.pop();
      if (surface[x] == surface[y]) continue;
      contract(x, y, eid);
    }
    while (!heap4.empty() && heap4.min() == time_current_) {
      int b = heap4.argmin(); heap4.pop();
      expand(b);
    }
    return false;
  }

 private:
  void initialize() {
    que = Queue<int>(N);
    mate.assign(S, 0);
    link.assign(S, {0, 0});
    label.assign(S, kFree);
    base.resize(S); for (int u = 1; u < S; ++u) base[u] = u;
    surface.resize(S); for (int u = 1; u < S; ++u) surface[u] = u;

    potential.resize(S);
    node.resize(S); for (int b = 1; b < S; ++b) node[b] = Node(b);

    unused_bid.resize(B); for (int i = 0; i < B; ++i) unused_bid[i] = N + B - i;
    unused_bid_idx_ = B;

    // for O(nm log n) implementation
    reset_time();
    time_created.resize(S);
    slack.resize(S); for (int i = 0; i < S; ++i) slack[i] = Inf;
    best_from.assign(S, 0);
    heavy.assign(S, 0);
    lazy.assign(S, 0);
    group.resize(S); for (int i = 0; i < S; ++i) group[i] = i;
  }

  void set_potential() {
    for (int u = 1; u <= N; ++u) {
      cost_t max_c = 0;
      for (int eid = ofs[u]; eid < ofs[u + 1]; ++eid) {
        max_c = max(max_c, edges[eid].cost);
      }
      potential[u] = max_c >> 1;
    }
  }

  void find_maximal_matching() {
    // Find a maximal matching naively.
    for (int u = 1; u <= N; ++u) if (!mate[u]) {
      for (int eid = ofs[u]; eid < ofs[u + 1]; ++eid) {
        auto &e = edges[eid]; int v = e.to;
        if (mate[v] > 0 || reduced_cost(u, v, e) > 0) continue;
        mate[u] = v; mate[v] = u;
        break;
      }
    }
  }

 private:
  int N, B, S; // N = |V|, B = (|V| - 1) / 2, S = N + B + 1
  vector<int> ofs;
  vector<Edge> edges;

  Queue<int> que;
  vector<int> mate, surface, base;
  vector<Link> link;
  vector<Label> label;
  vector<cost_t> potential;

  vector<int> unused_bid; int unused_bid_idx_;
  vector<Node> node;

  // for O(nm log n) implementation
  vector<int> heavy, group;
  vector<cost_t> time_created, lazy, slack;
  vector<int> best_from;

  cost_t time_current_;
  Event event1;
  ModifiableHeap<EdgeEvent> heap2;
  ModifiableHeaps<EdgeEvent> heap2s;
  FastHeap<EdgeEvent> heap3;
  ModifiableHeap<cost_t> heap4;
};

using cost_t = int;
using tcost_t = long long;

using MWM = MaximumWeightedMatching<cost_t, tcost_t>;
using Edge = MWM::InputEdge;

pair<tcost_t, vector<int>> maximum_weighted_matching(int n, vector<int> from, vector<int> to, vector<cost_t> cost) {
  int m = (int) cost.size();
  vector<Edge> edges(m * 2);
  vector<int> ou(n + 2), ov(n + 2);
  for (int i = 0; i < m; ++i) {
    edges[i] = {from[i] + 1, to[i] + 1, cost[i]};
    ++ou[from[i] + 2];
    ++ov[to[i] + 2];
  }
  for (int i = 1; i <= n + 1; ++i) {
    ov[i] += ov[i - 1];
  }
  for (int i = 0; i < m; ++i) {
    edges[m + (ov[edges[i].to]++)] = edges[i];
  }
  for (int i = 1; i <= n + 1; ++i) {
    ou[i] += ou[i - 1];
  }
  for (int i = 0; i < m; ++i) {
    edges[ou[edges[i + m].from]++] = edges[i + m];
  }
  edges.resize(m);
  pair<tcost_t, vector<int>> ans = MWM(n, edges).maximum_weighted_matching();
  for (int i = 0; i < n; ++i) {
    ans.second[i] = ans.second[i + 1] - 1;
  }
  ans.second.resize(n);
  return ans;
}

const int inf = 0x3f3f3f3f;

struct point {
  int x, y;
  
  point(int x = 0, int y = 0): x(x), y(y) {
  }

  point operator - (const point &other) const {
    return point(x - other.x, y - other.y);
  }

  bool operator < (const point &other) const {
    if (where() != other.where()) {
      return where() < other.where();
    } else {
      return *this * other > 0;
    }
  }

  int operator * (const point &other) const {
    return x * other.y - y * other.x;
  }

  int where() const {
    return y > 0 || (!y && x < 0);
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<point> p(n);
  for (int i = 0; i < n; ++i) {
    cin >> p[i].x >> p[i].y;
  }
  vector<int> from(m), to(m), cost(m);
  vector<vector<pair<int, int>>> nearby(n);
  for (int i = 0; i < m; ++i) {
    cin >> from[i] >> to[i] >> cost[i];
    --from[i];
    --to[i];
    nearby[from[i]].emplace_back(to[i], i * 2);
    nearby[to[i]].emplace_back(from[i], i * 2 + 1);
  }
  for (int i = 0; i < n; ++i) {
    sort(nearby[i].begin(), nearby[i].end(), [&](const pair<int, int> &a, const pair<int, int> &b) {
      return p[a.first] - p[i] < p[b.first] - p[i];
    });
  }
  vector<int> id(m * 2, -1);
  int dual_n = 0;
  for (int i = 0; i < m * 2; ++i) {
    if (id[i] == -1) {
      int e = i;
      while (id[e] == -1) {
        id[e] = dual_n;
        int x = from[e / 2], y = to[e / 2];
        if (!(e & 1)) {
          swap(x, y);
        }
        int w = lower_bound(nearby[x].begin(), nearby[x].end(), make_pair(y, e ^ 1), [&](const pair<int, int> &a, const pair<int, int> &b) {
          return p[a.first] - p[x] < p[b.first] - p[x];
        }) - nearby[x].begin();
        e = nearby[x][(w + 1) % nearby[x].size()].second;
      }
      ++dual_n;
    }
  }
  vector<vector<pair<int, int>>> adj(dual_n);
  for (int i = 0; i < m; ++i) {
    if (id[i * 2] != id[i * 2 + 1]) {
      adj[id[i * 2]].emplace_back(id[i * 2 + 1], i);
      adj[id[i * 2 + 1]].emplace_back(id[i * 2], i);
    }
  }
  vector<vector<int>> dist(dual_n, vector<int>(dual_n, inf));
  vector<vector<pair<int, int>>> pre(dual_n, vector<pair<int, int>>(dual_n, make_pair(-1, -1)));
  for (int i = 0; i < dual_n; ++i) {
    priority_queue<pair<int, int>> q;
    vector<bool> visit(dual_n);
    dist[i][i] = 0;
    q.emplace(0, i);
    while (!q.empty()) {
      int x = q.top().second;
      q.pop();
      if (visit[x]) {
        continue;
      }
      visit[x] = true;
      for (auto p : adj[x]) {
        int y = p.first, w = cost[p.second];
        if (dist[i][y] > dist[i][x] + w) {
          dist[i][y] = dist[i][x] + w;
          pre[i][y] = make_pair(x, p.second);
          q.emplace(-dist[i][y], y);
        }
      }
    }
  }
  vector<int> dual_from, dual_to, dual_cost;
  for (int i = 0; i < dual_n; ++i) {
    if (adj[i].size() % 2) {
      for (int j = 0; j < i; ++j) {
        if (adj[j].size() % 2) {
          dual_from.push_back(i);
          dual_to.push_back(j);
          dual_cost.push_back(inf - dist[i][j]);
        }
      }
    }
  }
  vector<int> match = maximum_weighted_matching(dual_n, dual_from, dual_to, dual_cost).second;
  vector<bool> pass(m);
  for (int i = 0; i < dual_n; ++i) {
    if (i < match[i]) {
      int x = i, y = match[i];
      while (x != y) {
        pass[pre[x][y].second] = true;
        y = pre[x][y].first;
      }
    }
  }
  int ans = 0;
  vector<vector<int>> bipartite(n);
  for (int i = 0; i < m; ++i) {
    if (!pass[i]) {
      ans += cost[i];
      bipartite[from[i]].push_back(to[i]);
      bipartite[to[i]].push_back(from[i]);
    }
  }
  vector<int> color(n, -1);
  function<void(int)> dfs = [&](int x) {
    for (auto y : bipartite[x]) {
      if (color[y] == -1) {
        color[y] = !color[x];
        dfs(y);
      }
    }
  };
  for (int i = 0; i < n; ++i) {
    if (color[i] == -1) {
      color[i] = 0;
      dfs(i);
    }
  }
  cout << ans << "\n";
  for (int i = 0; i < n; ++i) {
    if (i) {
      cout << " ";
    }
    cout << color[i];
  }
  cout << "\n";
  return 0;
}

Battle Royale

不妨令 L = 0 L=0 L=0 ,当 x < M x<M x<M 时,其存活时间 t i = x i R M + a i t_i = \frac{x_iR}{M}+a_i ti=MxiR+ai ,维护一个上凸壳,再用单调栈维护当前 M M M 的最大 t i t_i ti 即可。对于另一个方向同理,最后可以得到若干段,每段有两个候选点,它们作为最优解的 M M M 一定存在一个分界点,二分即可。

#include 

using namespace std;

typedef long double ld;

struct point {
  long long x, y;

  point(long long x = 0, long long y = 0): x(x), y(y) {
  }

  point operator - (const point &other) const {
    return point(x - other.x, y - other.y);
  }

  long long operator * (const point &other) const {
    return x * other.y - y * other.x;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cout.setf(ios::fixed);
  cout.precision(20);
  int n, l, r;
  cin >> n >> l >> r;
  r -= l;
  vector<int> x(n);
  for (int i = 0; i < n; ++i) {
    cin >> x[i];
    x[i] -= l;
  }
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
  }
  vector<vector<pair<ld, int>>> events(2);
  for (int rep = 0; rep < 2; ++rep) {
    events[rep].emplace_back(0, -1);
    vector<point> p(n);
    for (int i = 0; i < n; ++i) {
      p[i] = point((long long) x[i] * r, a[i]);
    }
    vector<int> ch;
    for (int i = 0; i < n; ++i) {
      while (!ch.empty() && p[ch.back()].y <= p[i].y) {
        ch.pop_back();
      }
      while ((int) ch.size() > 1 && (p[ch.back()] - p[ch[ch.size() - 2]]) * (p[i] - p[ch[ch.size() - 2]]) >= 0) {
        ch.pop_back();
      }
      ch.push_back(i);
      ld low = x[i], high = i == n - 1 ? r : x[i + 1];
      while ((int) ch.size() > 1) {
        ld mid = (ld) (p[ch.back()].x - p[ch[ch.size() - 2]].x) / (p[ch[ch.size() - 2]].y - p[ch.back()].y);
        if (mid < low) {
          ch.pop_back();
        } else if (mid < high) {
          events[rep].emplace_back(low, ch.back());
          low = mid;
          ch.pop_back();
        } else {
          events[rep].emplace_back(low, ch.back());
          low = high;
          break;
        }
      }
      if (low < high) {
        events[rep].emplace_back(low, ch.back());
      }
    }
    events[rep].emplace_back(r, -1);
    reverse(a.begin(), a.end());
    reverse(x.begin(), x.end());
    for (int i = 0; i < n; ++i) {
      x[i] = r - x[i];
    }
  }
  reverse(events[1].begin(), events[1].end());
  for (auto &p : events[1]) {
    p.first = r - p.first;
    if (p.second != -1) {
      p.second = n - 1 - p.second;
    }
  }
  for (int i = 0; i + 1 < (int) events[1].size(); ++i) {
    events[1][i].second = events[1][i + 1].second;
  }
  vector<ld> ans(n);
  for (int i = 0, j = 0, left = -1, right = -1; i + 1 < (int) events[0].size() || j + 1 < (int) events[1].size(); ) {
    ld low, high;
    if (j + 1 == (int) events[1].size() || (i + 1 < (int) events[0].size() && events[0][i].first < events[1][j].first)) {
      left = events[0][i].second;
      low = events[0][i].first;
      ++i;
      high = min(events[0][i].first, events[1][j].first);
    } else {
      right = events[1][j].second;
      low = events[1][j].first;
      ++j;
      high = min(events[0][i].first, events[1][j].first);
    }
    if (left == -1 && right == -1) {
      continue;
    }
    auto which = [&](ld mid) {
      if (left == -1 || right == -1) {
        return left + right + 1;
      } else if ((long long) x[left] * r / mid + a[left] > (long long) (r - x[right]) * r / (r - mid) + a[right]) {
        return left;
      } else {
        return right;
      }
    };
    int foo = which(low), bar = which(high);
    if (foo == bar) {
      ans[foo] += high - low;
    } else {
      ld l = low, r = high;
      for (int i = 0; i < 60; ++i) {
        ld mid = (l + r) / 2;
        if (which(mid) == foo) {
          l = mid;
        } else {
          r = mid;
        }
      }
      ans[left] += l - low;
      ans[right] += high - r;
    }
  }
  for (int i = 0; i < n; ++i) {
    cout << ans[i] / r << "\n";
  }
  return 0;
}

Jeopardy

答案是 max ⁡ \max max 每行的最小值。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  int ans = 0;
  for (int i = 0; i < n; ++i) {
    int foo = 100;
    for (int j = 0; j < n; ++j) {
      int bar;
      cin >> bar;
      foo = min(foo, bar);
    }
    ans = max(ans, foo);
  }
  cout << ans << "\n";
  return 0;
}

Slippers

首先求出最大匹配,再考虑方向问题。如果有一个空余格子,则可以任意调整这个网格的方向;否则检验方向之和模 4 4 4 是否正确。

#include 

using namespace std;

const vector<int> dx = {-1, 0, 1, 0};
const vector<int> dy = {0, 1, 0, -1};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<vector<bool>> type(n, vector<bool>(m));
  vector<vector<int>> dir(n, vector<int>(m));
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j) {
      string s;
      cin >> s;
      type[i][j] = s[0] == 'R';
      dir[i][j] = s[1] == '^' ? 0 : s[1] == '>' ? 1 : s[1] == 'v' ? 2 : 3;
    }
  }
  vector<vector<int>> adj(n * m);
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j) {
      if (!type[i][j]) {
        for (int k = 0; k < 4; ++k) {
          int ii = i + dx[k], jj = j + dy[k];
          if (ii >= 0 && ii < n && jj >= 0 && jj < m && type[ii][jj]) {
            adj[i * m + j].push_back(ii * m + jj);
          }
        }
      }
    }
  }
  vector<int> match(n * m, -1);
  vector<int> visit(n * m, -1);
  int ans = 0;
  for (int i = 0; i < n * m; ++i) {
    if (!type[i / m][i % m]) {
      if (match[i] == -1) {
        function<bool(int)> dfs = [&](int x) {
          if (visit[x] == i) {
            return false;
          }
          visit[x] = i;
          for (auto y : adj[x]) {
            if (match[y] == -1 || dfs(match[y])) {
              match[x] = y;
              match[y] = x;
              return true;
            }
          }
          return false;
        };
        ans += dfs(i);
      }
    }
  }
  if (n * m % 2 == 1 || ans * 2 < n * m) {
    cout << ans << "\n";
    return 0;
  }
  int sum = 0;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j) {
      if (!type[i][j]) {
        int ii = match[i * m + j] / m, jj = match[i * m + j] % m;
        sum += dir[i][j] + dir[ii][jj];
        for (int k = 0; k < 4; ++k) {
          if (ii == i + dx[k] && jj == j + dy[k]) {
            sum -= (k + 3) * 2;
          }
        }
      }
    }
  }
  if (sum % 4) {
    ans = max(ans - 1, 0);
  }
  cout << ans << "\n";
  return 0;
}

The Good, the Bad and the Ugly

1 1 1 + + + 3 3 3 − - 9 9 9 + + + ,直到走到 0 0 0 ,然后就很好区分三种情况了。

#include 

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int len = 1;
  char c = '+';
  while (true) {
    for (int i = 0; i < len; ++i) {
      cout << c << endl;
      int zero;
      cin >> zero;
      if (zero) {
        cout << "+" << endl;
        cin >> zero;
        cout << "-" << endl;
        cin >> zero;
        if (zero) {
          cout << "! ugly" << endl;
        } else if (c == '+') {
          cout << "! bad" << endl;
        } else {
          cout << "! good" << endl;
        }
        return 0;
      }
    }
    c = '+' + '-' - c;
    len *= 3;
  }
  return 0;
}

你可能感兴趣的:(CodeForces Gym 102156 简要题解)