Submission #627257

#TimeUsernameProblemLanguageResultExecution timeMemory
627257model_codeThousands Islands (IOI22_islands)C++17
100 / 100
360 ms47916 KiB
// model_solution/solution.cpp
#include "islands.h"

#include <bits/stdc++.h>

#define edge std::pair<int,int>


int mode[111000];

std::vector<std::set<edge>> back_path(222000);
std::vector<std::set<edge>> path(222000);
std::queue<int> remove_q;

bool must_flip = false;

int path_1_loop, path_2_loop;


void put_path(std::vector<int> &sol, std::vector<int> &mirror_sol, std::vector<std::pair<int, int>> &path_1, int loop_id) {
  bool start = false;
  for (int i = 1; i < (int) path_1.size(); i++) {
      int u = path_1[i - 1].first;
      int v = path_1[i].first;

      if ((!start && u == loop_id) || (start && v == loop_id)) {
        start = 1;
        mirror_sol.push_back(1);
      }
      else
        mirror_sol.push_back(0);

      if (path_1[i].second == 1)
        sol.push_back(path[u].begin()->first);
      else
        sol.push_back(path[v].begin()->first);  
    }
}

// remove a node X
void remove_node(int X) {
  // remove node 'curr', and all paths from/to them
    for (auto e: back_path[X]) {
      auto e_id = e.first;
      auto v = e.second;
      path[v].erase({e_id, X});
      if ((int) path[v].size() == 0) remove_q.push(v);
    }

    for (auto e: path[X]) {
      auto e_id = e.first;
      auto v = e.second;
      back_path[v].erase({e_id, X});
    } 
}

// chain-remove all 0 outdegrees
void clear_unused_nodes() {
  while (remove_q.size()) {
    int curr = remove_q.front();
    remove_q.pop();

    remove_node(curr);  
  }
}

std::variant<bool, std::vector<int>> find_journey(int N, int M, std::vector<int> U, std::vector<int> V) {
  // construct the graph
  for (int i = 0; i < M; i++) {
    path[U[i]].insert({i, V[i]});
    back_path[V[i]].insert({i, U[i]});
  }

  for (int i = 0; i < N; i++)
    if ((int) path[i].size() == 0) remove_q.push(i);
  
  int START = 0;
  std::vector<int> backtrack;
  std::vector<int> init_sol;
  std::vector<int> back_sol;

  while (true) {
    clear_unused_nodes();
    // if START is removed, then no solution
    if ((int) path[START].size() == 0) return false;

    // if START outdeg is 1, move forward and start from there.



    if ((int) path[START].size() == 1) {
      backtrack.push_back(START);
      auto prev_start = START;
      init_sol.push_back(path[START].begin()->first);
      back_sol.push_back(path[START].begin()->first);

      START = path[START].begin()->second;
      
      remove_node(prev_start);
      continue;
    }

 
    // now, we start at START, which has >= 2 outdeg.
    // find a solution.

    // step 1: find a loop
    bool seen[111000];
    memset(seen, 0, sizeof(seen));

    // path notation: second denotes direction
    std::vector<std::pair<int, int>> path_1;
    std::vector<int> first_loop;


    int curr = START;

    while (true) {
      path_1.push_back({curr, 1});
      mode[curr] = 1;
      // loop found
      if (seen[curr]) {
        path_1_loop = curr;
        int X = path_1.size();
        
        bool reverse_mode = false;
        for (int i = X - 1; i >= 0; i--) {
          if (reverse_mode) {
            path_1.push_back({path_1[i].first, 0});
          }
          else {
            // the loop
            first_loop.push_back(path_1[i].first);
            mode[path_1[i].first] = 2;
          }
          if (i != X - 1 && path_1[i].first == curr) reverse_mode = true;
        }

        break;
      } 

      seen[curr] = 1;
      int next = path[curr].begin()->second;  /// guaranteed exists
      curr = next;
    }

    // step 2: find another loop
    // path notation: second denotes direction
    std::vector<std::pair<int, int>> path_2;

    curr = path[START].rbegin()->second;

    while (true) {

      path_2_loop = curr;
      path_2.push_back({curr, 1});
      if (seen[curr]) {
        // 3 cases:
        // 1st cases, found a straight path from path_1.
        if (mode[curr] == 1) {
          int curr2 = path[curr].begin()->second;

          while (mode[curr2] == 1) {
            path_2.push_back({curr2, 1});
            curr2 = path[curr2].begin()->second;
          }
          //backloop
          path_2.push_back({first_loop[0], 1});
          for (int i = 1; i < (int) first_loop.size(); i++) {
            path_2.push_back({first_loop[i], 0});
          }
          // set for trackback
          curr = first_loop[0];
        }

        else if (mode[curr] == 2) {
          // go back reverse circle
          // find the starting point
          int st = 0;
          for (int i = 0; i < (int) first_loop.size(); i++) {
            if (first_loop[i] == curr) {
              st = i + 1;
              break;
            }
          }

          // from st to end
          for (int i = st; i < (int) first_loop.size(); i++) {
            path_2.push_back({first_loop[i], 0});
          }
          // from begin to st
          for (int i = 1; i < st; i++) {
              path_2.push_back({first_loop[i], 0});
          }
        } else if (mode[curr] == 3) {
          must_flip = true;
        }

        int X = path_2.size();
        
        bool reverse_mode = false;
        for (int i = X - 1; i >= 0; i--) {
          if (reverse_mode) {
            path_2.push_back({path_2[i].first, 0});
          }
          if (i != X - 1 && path_2[i].first == curr) reverse_mode = true;
        }
        break;
      }
      mode[curr] = 3;
      seen[curr] = 1;
      curr = path[curr].begin()->second;
    }

    // has a solution
    // convert paths to edges
    std::vector<int> sol;
    std::vector<int> mirror_sol;


    put_path(sol, mirror_sol, path_1, path_1_loop);
    mirror_sol.push_back(0);
    sol.push_back(path[START].rbegin()->first);

    put_path(sol, mirror_sol, path_2, path_2_loop);
    
    mirror_sol.push_back(0);
    sol.push_back(path[START].rbegin()->first);

    
    if (must_flip) {
      int X = sol.size();
      std::stack<int> stack_path;
      bool stack_fill_mode = 0;

      for (int i = 0; i < X; i++) {
        while (!stack_fill_mode && stack_path.size()) {
          int tmp = stack_path.top();
          stack_path.pop();
          sol.push_back(tmp);
        }
        if (mirror_sol[i] == 1) {
          stack_fill_mode ^= 1;
        }

        if (stack_fill_mode)
          stack_path.push(sol[i]);
        else
          sol.push_back(sol[i]);
      }
    }


    for (auto x: sol){
      init_sol.push_back(x);
    }

    std::reverse(back_sol.begin(), back_sol.end());
    for (auto x: back_sol){
      init_sol.push_back(x);
    }

    return init_sol;
  }
}
#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...