Submission #1099903

#TimeUsernameProblemLanguageResultExecution timeMemory
1099903model_codeSphinx's Riddle (IOI24_sphinx)C++17
100 / 100
343 ms3272 KiB
// correct/solution-author-refactor.cpp

#include <bits/stdc++.h>

#include "sphinx.h"
using namespace std;

const int MAX_N = 250 + 10;

const int ACTUAL_COLOR = -1;

using pii = pair<int, int>;

struct ComponentCounter {
  int n;
  vector<vector<int>> adjlist;

  int TT;
  vector<int> seen;

  ComponentCounter(vector<vector<int>> _adjlist)
      : n(_adjlist.size()), adjlist(_adjlist), TT(0), seen(n) {}
  ComponentCounter() : ComponentCounter(vector<vector<int>>()) {}

  /*
   * Marks all vertices in u's connected color component as seen, so long as
   * u's color is constant and known as per proposed_colors.
   *
   * proposed_colors is a list of colors that would be used to query.
   * u is a starting vertex
   */
  // Mark all as seen from u.
  void dfs(vector<int> &proposed_colors, int u) {
    seen[u] = TT;
    for (auto v : adjlist[u]) {
      if (proposed_colors[u] != ACTUAL_COLOR &&
          proposed_colors[u] == proposed_colors[v] && seen[v] != TT) {
        dfs(proposed_colors, v);
      }
    }
  }

  /*
   * Return number of connected color-components among vertices, according to
   * proposed_colors (only among known colors).
   */
  int num_components(vector<int> &proposed_colors, vector<int> &vertices) {
    TT++;
    int ret = 0;
    for (auto u : vertices) {
      if (seen[u] != TT) {
        ret++;
        dfs(proposed_colors, u);
      }
    }
    return ret;
  }
};

struct Groups {
  int n;
  vector<int> groupOf;
  vector<vector<int>> members;

  Groups() : Groups(0) {}
  Groups(int _n) : n(_n), groupOf(_n), members(_n) {
    for (int i = 0; i < n; i++) {
      groupOf[i] = i;
      members[i].push_back(i);
    }
  }

  int group_of(int x) { return groupOf[x]; }

  void join_groups_of(int x, int y) {
    int gx = group_of(x);
    int gy = group_of(y);

    if (members[gx].size() > members[gy].size()) {
      swap(gx, gy);
    }

    for (auto u : members[gx]) {
      groupOf[u] = gy;
      members[gy].push_back(u);
    }
    members[gx].clear();
  }

  vector<int> non_empty_groups() {
    vector<int> res;
    for (int i = 0; i < n; i++) {
      if (!members[i].empty()) {
        res.push_back(i);
      }
    }
    return res;
  }

  // Moves groups to the front
  void compress() {
    auto non_empty_group_indices = non_empty_groups();
    int upto = 0;
    for (auto g : non_empty_group_indices) {
      for (auto u : members[g]) {
        groupOf[u] = upto;
      }
      upto++;
    }
    for (int i = 0; i < n; i++) {
      members[i].clear();
    }
    for (int i = 0; i < n; i++) {
      members[groupOf[i]].push_back(i);
    }
  }

  /*
   * Facilitate a query on the "grouping" graph.
   *
   * This is equivalent to setting all vertices in a group to the group's color.
   * If group's color is ACTUAL_COLOR, then all vertices in the group will be
   * set to ACTUAL_COLOR. Note that this color must be the same for all vertices
   * in that group, since vertices in the same group belong to the same
   * color-connected component.
   *
   * Returns the number of color-connected components.
   * Note that this is always the same in the grouping graph as in the original
   * graph.
   */
  int queryGroups(vector<int> mask) {
    vector<int> proposed_colors(n);
    for (int i = 0; i < n; i++) {
      proposed_colors[i] = mask[groupOf[i]];
    }
    return perform_experiment(proposed_colors);
  }
};

/*
 * Identifies the color-connected components, but not their exact colors.
 */
struct Collapser {
  ComponentCounter componentCounter;
  int n;
  Groups grouper;

  Collapser(ComponentCounter _componentCounter)
      : componentCounter(_componentCounter),
        n(_componentCounter.n),
        grouper(n) {}

  // query for number of components in groups [s, e] union {cur}
  int queryOnly(vector<int> &groups, int s, int e, int cur) {
    vector<int> proposed_colors(n, n);
    for (int i = s; i < e; i++) {
      for (auto u : grouper.members[groups[i]]) {
        proposed_colors[u] = ACTUAL_COLOR;
      }
    }
    proposed_colors[cur] = ACTUAL_COLOR;

    vector<int> others;
    for (int i = 0; i < n; i++) {
      if (proposed_colors[i] == n) {
        others.push_back(i);
      }
    }
    int others_components =
        componentCounter.num_components(proposed_colors, others);

    auto found = perform_experiment(proposed_colors);
    auto res = found - others_components;
    return res;
  }

  // how many edges are from cur to vertices in groups [s, e) ?
  int extra_edges(vector<int> &groups, int s, int e, int cur) {
    auto if_none = e - s + 1;
    auto components = queryOnly(groups, s, e, cur);
    auto edges = if_none - components;
    return edges;
  }

  // there are num_edges edges from cur to vertices in groups [s,e).
  // find them and unionise with cur.
  void solve(vector<int> &groups, int s, int e, int num_edges, int cur) {
    if (num_edges == 0) return;
    if (e - s == num_edges) {
      for (int i = s; i < e; i++) {
        auto u = grouper.members[groups[i]].front();
        grouper.join_groups_of(cur, u);
      }
      return;
    }

    int mid = (e + s) / 2;

    auto left_edges = extra_edges(groups, s, mid, cur);
    solve(groups, s, mid, left_edges, cur);
    solve(groups, mid, e, num_edges - left_edges, cur);
  }

  void go() {
    for (int i = 1; i < n; i++) {
      auto non_empty_groups = grouper.non_empty_groups();
      vector<int> groups;
      for (auto g : non_empty_groups) {
        if (g < i) {
          groups.push_back(g);
        }
      }
      auto edges = extra_edges(groups, 0, groups.size(), i);
      solve(groups, 0, groups.size(), edges, i);
    }
  }

  Groups find_colors() {
    go();
    return grouper;
  }
};

struct TwoColor {
  vector<vector<int>> adjlist;
  vector<int> mark;

  // Defines a connected graph
  TwoColor(vector<vector<int>> _adjlist)
      : adjlist(_adjlist), mark(adjlist.size(), -1) {}

  void dfs(int u) {
    for (auto v : adjlist[u]) {
      if (mark[v] == -1) {
        mark[v] = mark[u] ^ 1;
        dfs(v);
      }
    }
  }

  // Returns a vector which is a 2-coloring of the graph, starting at 0.
  vector<int> two_color() {
    mark[0] = 0;
    dfs(0);
    return mark;
  }
};

// solution to subtask where graph is a coloring
namespace coloring {
Groups grouper;
ComponentCounter componentCounter;
vector<int> proposed_colors;

int known_colors[MAX_N];

// Find all of a particular color in subject, and write it to known_colors, and
// return it. Invariant: at least one of color in subject. Invariant:
// proposed_colors of all of test is color, proposed_colors of all of subject is
// ACTUAL_COLOR (or the actual color). here is number of components for
// proposed_colors
vector<int> find_of_color(int color, vector<int> &subject, vector<int> &test,
                          int here) {
  vector<int> ret;
  if (subject.size() <= 1) {
    for (auto u : subject) {
      known_colors[u] = color;
      ret.push_back(u);
    }
    return ret;
  }

  auto mid = subject.begin() + (subject.size() / 2);
  auto left = vector<int>(subject.begin(), mid);
  auto right = vector<int>(mid, subject.end());

  // check if any on left first
  for (auto u : right) {
    test.push_back(u);
    proposed_colors[u] = color;
  }
  int if_none =
      left.size() + componentCounter.num_components(proposed_colors, test);
  auto actual = grouper.queryGroups(proposed_colors);

  if (actual < if_none) {
    // there's some on the left
    ret = find_of_color(color, left, test, actual);
  }

  for (auto u : right) {
    test.pop_back();
    proposed_colors[u] = ACTUAL_COLOR;
  }

  // check if left accounts for everything
  for (auto u : left) {
    test.push_back(u);
    if (known_colors[u] == color) {
      proposed_colors[u] = color;
    }
  }
  auto test_components = componentCounter.num_components(proposed_colors, test);
  int if_none_right = right.size() + test_components;

  if (here < if_none_right) {
    // there's some on the right
    auto ret_right = find_of_color(color, right, test, here);
    for (auto u : ret_right) {
      ret.push_back(u);
    }
  }
  for (auto u : left) {
    proposed_colors[u] = ACTUAL_COLOR;
    test.pop_back();
  }

  return ret;
}

vector<int> find_colors(Groups _grouper, ComponentCounter _componentCounter,
                        int num_colors) {
  grouper = _grouper;
  componentCounter = _componentCounter;
  int n = componentCounter.n;
  for (int i = 0; i < n; i++) {
    proposed_colors.push_back(ACTUAL_COLOR);
    known_colors[i] = -1;
  }

  if (grouper.non_empty_groups().size() == 1) {
    for (int f = 0; f < num_colors; ++f) {
      vector<int> ord(num_colors, f);
      ord[0] = -1;
      if (perform_experiment(ord) == 1) return {f};
    }
  }

  vector<int> parts[2];
  int comp[2];

  TwoColor twoColor(componentCounter.adjlist);
  auto marks = twoColor.two_color();
  for (int i = 0; i < n; i++) {
    parts[marks[i]].push_back(i);
  }

  for (int z = 0; z < 2; z++) {
    for (auto u : parts[z]) {
      proposed_colors[u] = 0;
    }
    comp[z] = componentCounter.num_components(proposed_colors, parts[z]);
    for (auto u : parts[z]) {
      proposed_colors[u] = ACTUAL_COLOR;
    }
  }

  for (int c = 0; c < num_colors; c++) {
    for (int z = 0; z < 2; z++) {
      for (auto u : parts[z ^ 1]) {
        proposed_colors[u] = c;
      }
      int if_none = parts[z].size() + comp[z ^ 1];
      auto init = grouper.queryGroups(proposed_colors);

      if (init < if_none) {
        find_of_color(c, parts[z], parts[z ^ 1], init);
      }
      for (auto u : parts[z ^ 1]) {
        proposed_colors[u] = ACTUAL_COLOR;
      }
    }
  }

  return vector<int>(known_colors, known_colors + n);
}
}  // namespace coloring

vector<int> find_colours(int n, vector<int> a, vector<int> b) {
  vector<vector<int>> adjlist(n);
  int m = a.size();
  for (int i = 0; i < m; i++) {
    auto u = a[i];
    auto v = b[i];
    adjlist[u].push_back(v);
    adjlist[v].push_back(u);
  }
  ComponentCounter componentCounter(adjlist);

  // Returned solution has the property that every color component has a
  // distinct value. We use this for grouping.
  Collapser collapser(componentCounter);
  auto collapsed_groups = collapser.find_colors();
  collapsed_groups.compress();

  int collapsedNodes = collapsed_groups.non_empty_groups().size();
  vector<vector<int>> collapsedAdjlist(collapsedNodes);
  for (int u = 0; u < collapsedNodes; u++) {
    unordered_set<int> neighbors;
    for (auto uMember : collapsed_groups.members[u]) {
      for (auto vMember : adjlist[uMember]) {
        auto v = collapsed_groups.group_of(vMember);
        if (u != v) {
          neighbors.insert(v);
        }
      }
    }
    for (auto v : neighbors) {
      collapsedAdjlist[u].push_back(v);
    }
  }
  ComponentCounter collapsedCounter(collapsedAdjlist);

  auto groupedColors =
      coloring::find_colors(collapsed_groups, collapsedCounter, n);
  vector<int> known_colors;
  for (int i = 0; i < n; i++) {
    known_colors.push_back(groupedColors[collapsed_groups.group_of(i)]);
  }

  return known_colors;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...