Submission #1208815

#TimeUsernameProblemLanguageResultExecution timeMemory
1208815spacewalker밀림 점프 (APIO21_jumps)C++20
68 / 100
4077 ms138836 KiB
#include "jumps.h"

#include <bits/stdc++.h>

using namespace std;

vector<int> next_greater(const vector<int> &v) {
  int n = v.size();
  vector<int> ans(n, -1);
  vector<int> possible_next{n - 1};
  for (int i = n - 2; i >= 0; --i) {
    while (!possible_next.empty() && v[i] >= v[possible_next.back()])
      possible_next.pop_back();
    if (!possible_next.empty())
      ans[i] = possible_next.back();
    possible_next.push_back(i);
  }
  return ans;
}

using interval = pair<int, int>;

interval onion(interval a, interval b) {
  return {min(a.first, b.first), max(a.second, b.second)};
}

interval intersection(interval a, interval b) {
  return {max(a.first, b.first), min(a.second, b.second)};
}

bool has_overlap(interval a, interval b) {
  return !(a.second < b.first || b.second < a.first);
}

// fn ((a, b), (a, b)) { (a, b) }
// let onion((x, y), (z, w)) = (min(x, z), max(y, w))

struct Jumper {
  vector<vector<int>> tree_ch;
  vector<vector<int>> locations;
  vector<int> depth;
  vector<pair<int, int>> reachable_from;
  vector<vector<pair<int, int>>> ranges_crossed;
  void init_reachable_from(int v) {
    reachable_from[v] = {v, v};
    for (int ch : tree_ch[v]) {
      depth[ch] = depth[v] + 1;
      init_reachable_from(ch);
      reachable_from[v] = onion(reachable_from[v], reachable_from[ch]);
    }
  }
  Jumper() {}
  Jumper(int root, const vector<int> &tree,
         const vector<pair<int, int>> init_ranges)
      : tree_ch(tree.size()), locations(20, vector(tree.size(), -1)),
        depth(tree.size()),
        reachable_from(tree.size(), {-1, -1}),
        ranges_crossed(
            20, vector(tree.size(), pair<int, int>{tree.size() + 100, -1})) {
    for (int i = 0; i < tree.size(); ++i)
      if (tree[i] != -1) {
        tree_ch[tree[i]].push_back(i);
        locations[0][i] = tree[i];
      }

    init_reachable_from(root);
    ranges_crossed[0] = init_ranges;
    for (int i = 0; i < tree.size(); ++i) {
      int layer = 0;
      // cerr << "layer " << layer << " " << i << " -> " << locations[layer][i]
      //      << " (" << ranges_crossed[layer][i].first << ", "
      //      << ranges_crossed[layer][i].second << ")" << endl;
    }
    for (int layer = 1; layer < locations.size(); ++layer) {
      for (int i = 0; i < tree.size(); ++i)
        if (locations[layer - 1][i] != -1) {
          locations[layer][i] = locations[layer - 1][locations[layer - 1][i]];
          ranges_crossed[layer][i] =
              onion(ranges_crossed[layer - 1][i],
                    ranges_crossed[layer - 1][locations[layer - 1][i]]);
          // cerr << "layer " << layer << " " << i << " -> " << locations[layer][i]
          //      << " (" << ranges_crossed[layer][i].first << ", "
          //      << ranges_crossed[layer][i].second << ")" << endl;
        }
    }
  }

  int last_inside(int v, interval i) {
    int cv = v;
    for (int layer = locations.size() - 1; layer >= 0; --layer) {
      if (locations[layer][cv] != -1 && i.first <= locations[layer][cv] &&
          locations[layer][cv] <= i.second)
        cv = locations[layer][cv];
    }
    return cv;
  }

  // {vertex, # of steps taken, interval visited}
  tuple<int, int, interval> last_before_overlapping(int v, interval i) {
    int cv = v;
    auto crange = ranges_crossed[0][v];
    int dist = 0;
    for (int layer = locations.size() - 1; layer >= 0; --layer) {
      if (locations[layer][cv] != -1 &&
          !has_overlap(onion(crange, ranges_crossed[layer][cv]), i)) {
        crange = onion(crange, ranges_crossed[layer][cv]);
        cv = locations[layer][cv];
        dist += (1 << layer);
      }
    }
    return {cv, dist, crange};
  }

  int direct_len(int v, interval i) {
    auto [dst, dist, _] = last_before_overlapping(v, i);
    if (i.first <= locations[0][dst] && locations[0][dst] <= i.second) {
      return dist + 1;
    } else
      return -1;
  }

  int parent(int v) { return locations[0][v]; }

  interval reachable_range(int v) { return reachable_from[v]; }

  int lca(int i, int j) {
    if (i == j) return i;
    if (depth[i] < depth[j]) swap(i, j);
    for (int layer = locations.size() - 1; layer >= 0; --layer) {
      if (locations[layer][i] != -1 && depth[locations[layer][i]] >= depth[j]) i = locations[layer][i];
    }
    if (i == j) return i;
    for (int layer = locations.size() - 1; layer >= 0; --layer) {
      if (locations[layer][i] != -1 && locations[layer][i] != locations[layer][j]) {
        i = locations[layer][i];
        j = locations[layer][j];
      }
    }
    return locations[0][i];
  }
};

Jumper fast, slow;

vector<int> H;

void init(int N, std::vector<int> _H) {
  H = _H;
  // impl detail so ng and pg are always defined on nodes that matter
  vector<int> ng = next_greater(H);
  reverse(H.begin(), H.end());
  vector<int> pg = next_greater(H);
  reverse(H.begin(), H.end());
  reverse(pg.begin(), pg.end());
  for (int &v : pg)
    v = (v == -1 ? -1 : N - 1 - v);

  vector<int> fast_tree(N, -1), slow_tree(N, -1);
  for (int i = 0; i < N; ++i) {
    if (ng[i] == -1 && pg[i] == -1)
      continue;
    else if (ng[i] == -1 || pg[i] == -1) {
      int par = max(ng[i], pg[i]);
      fast_tree[i] = slow_tree[i] = par;
    } else {
      pair<int, int> target1 = {H[ng[i]], ng[i]}, target2 = {H[pg[i]], pg[i]};
      if (target1 > target2)
        swap(target1, target2);
      fast_tree[i] = target2.second;
      slow_tree[i] = target1.second;
    }
    // cerr << "jump targets: " << fast_tree[i] << " " << slow_tree[i] << endl;
  }
  int max_idx = distance(begin(H), max_element(begin(H), end(H)));
  // get ranges for the slow tree
  vector slow_tree_init_ranges(N, pair{-1, -1});
  for (int i = 0; i < N; ++i) {
    slow_tree_init_ranges[i] = {min(i, slow_tree[i]), max(i, slow_tree[i])};
  }
  // cerr << " == processing slow tree " << endl;
  slow = Jumper(max_idx, slow_tree, slow_tree_init_ranges);

  // from traversal info on the slow tree, get ranges for the fast tree
  vector fast_tree_init_ranges(N, pair{-1, -1});
  for (int i = 0; i < N; ++i) {
    if (fast_tree[i] != -1) {
      pair ft_int{fast_tree[i], fast_tree[i]};
      pair slow_tree_int = get<2>(slow.last_before_overlapping(i, ft_int));
      fast_tree_init_ranges[i] = onion(slow_tree_int, ft_int);
    }
  }
  // cerr << " == processing fast tree " << endl;
  fast = Jumper(max_idx, fast_tree, fast_tree_init_ranges);
}

int brute_force_minimum_jumps(int A, int B, int C, int D) {
  vector<int> previ(H.size(), -1), nexti(H.size(), -1);
  for (int cur = 0; cur < H.size(); ++cur) {
    for (int i = cur - 1; i >= 0; --i)
      if (H[i] > H[cur]) {
        previ[cur] = i;
        break;
      }
    for (int j = cur + 1; j < H.size(); ++j)
      if (H[j] > H[cur]) {
        nexti[cur] = j;
        break;
      }
  }
  int ans = -1;
  for (int start = A; start <= B; ++start) {
    vector<int> dist(H.size(), -1);
    queue<int> q;
    q.push(start);
    dist[start] = 0;
    while (!q.empty()) {
      int cur = q.front();
      q.pop();
      if (C <= cur && cur <= D) {
        ans = (ans == -1 ? dist[cur] : min(ans, dist[cur]));
      }
      int prev = previ[cur], next = nexti[cur];
      if (prev != -1 && dist[prev] == -1) {
        dist[prev] = dist[cur] + 1;
        q.push(prev);
      }
      if (next != -1 && dist[next] == -1) {
        dist[next] = dist[cur] + 1;
        q.push(next);
      }
    }
  }
  return ans;
}

int minimum_jumps_single_source(int src, int C, int D, interval possible_sources) {
  // cerr << "MJSS " << src << endl;
  if (possible_sources.first > src || possible_sources.second < src) return -1;
  // cerr << " starting from " << src << endl;
  int ans = -1;;
  auto consider = [&] (int v) {
    if (ans == -1 || (v != -1 && v < ans)) ans = v;
  };
  // take as many fast edges as possible before our path, traced along
  // the slow edges, hits {C, D}
  auto [v1, dist1, _] = fast.last_before_overlapping(src, {C, D});
  // cerr << " phase 1: " << v1 << " in " << dist1 << " jumps" << endl;
  int v1p = fast.parent(v1);
  if (C <= v1p && v1p <= D) {
    // case 1: another fast edge gets us there
    consider(dist1 + 1); 
  } else {
    // case 2: we get there using only slow edges
    auto [v2, dist2, __] = slow.last_before_overlapping(v1, {C, D});

    int pot_dst = slow.parent(v2);
    // cerr << " phase 2: " << v2 << " in " << dist2 << " jumps -> " << pot_dst << endl;
    if (C <= pot_dst && pot_dst <= D)
       consider(dist1 + dist2 + 1);

    // case 3: we need to take another fast edge
    // MAGIC CLAIM: This only happens O(sqrt(n)) times
    int further_dist = minimum_jumps_single_source(v1p, C, D, possible_sources);
    if (further_dist != -1) consider(minimum_jumps_single_source(v1p, C, D, possible_sources) + dist1 + 1);
  }
  // cerr << "MJSS " << src << " ret " << ans << endl;
  return ans;
}

int minimum_jumps_real(int A, int B, int C, int D) {

  interval reachable_from_dst =
      slow.reachable_range(slow.lca(C, D));
  
  // cerr << "rfdst " << reachable_from_dst.first << " " << reachable_from_dst.second << endl;
  auto [realA, realB] = intersection({A, B}, reachable_from_dst);
  if (realA > realB) return -1;

  int src = slow.lca(realA, realB);

  return minimum_jumps_single_source(src, C, D, reachable_from_dst);
}

int minimum_jumps(int A, int B, int C, int D) {
  int ans = minimum_jumps_real(A, B, C, D);
/*  int bans = brute_force_minimum_jumps(A, B, C, D);
  cerr << "expected " << bans << " got " << ans << endl;
  assert(ans == bans);*/
  return ans;
}
#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...
#Verdict Execution timeMemoryGrader output
Fetching results...