top of page
Click here to go to the home page of AskTheCode.

Chef and Pairs Codechef - July Long Challenge Solution in C++

Team ATC

Updated: Jul 13, 2021

Codechef July Long Challenge 2021 Solution | Chef and Pairs solution in C++ | AskTheCode

 

Problem:

You are given a tree (connected, undirected, acyclic graph) consisting of N nodes. Based on this tree, you have to answer Q queries.


Each query is of the form:

  • K D V1 V2 ⋯ VK - output the number of pairs (i, j),1≤i<jK, such that the shortest path between nodes Vi and Vj in the tree has D edges.

 

Input:

  • The first line contains an integer T, the number of test cases. Then the test cases follow.

  • The first line of each test case contains two integers, N and Q.

  • N−1 lines follow. Each line consists of two integers u and v, indicating that there is an edge between nodes u and v in the tree.

  • Q lines follow. Each line describes a query in the format given above.


Output:

For each query, output the answer on a new line.

 

Sample Input:

1
5 2
1 2
1 3
2 4
4 5
3 2 2 3 5
2 4 1 3

Sample Output:

2
0
 

EXPLANATION:

In the first query, the pairs of nodes (2,3) and (2,5) have distance 2.


In the second query, there is no pair with distance 4.

 

Code:


#include <bits/stdc++.h>
using namespace std;
#define _USE_MATH_DEFINES

struct rooted_tree {
  vector<vector<int>> adj;
  vector<int> parent, depth, subtree, start, preorder, in, out;
  const vector<int>& operator [] (int i) const { return adj[i]; }
  rooted_tree(const vector<vector<int>>& adj_list, int root): adj(adj_list),
    parent(adj.size(), -1), depth(adj.size()), subtree(adj.size()), start(adj.size()),
    in(adj.size()), out(adj.size()) {
    preorder.reserve(adj.size());
    build(root, -1, 0);
  }
  rooted_tree(vector<vector<int>>&& adj_list, int root): adj(move(adj_list)),
    parent(adj.size(), -1), depth(adj.size()), subtree(adj.size()), start(adj.size()),
    in(adj.size()), out(adj.size()) {
    preorder.reserve(adj.size());
    build(root, -1, 0);
  }

  bool is_ancestor_of(int anc, int v, bool strict = true) const {
    if (strict) return in[anc] < in[v] && out[anc] > out[v];
    else return in[anc] <= in[v] && out[anc] >= out[v];
  }

private:
  int build(int u, int par, int idx) {
    in[u] = idx++;
    start[u] = (int)preorder.size();
    preorder.push_back(u);
    parent[u] = par;
    subtree[u] = 1;
    if (par != -1) {
      depth[u] = depth[par] + 1;
      vector<int>::iterator it = find(adj[u].begin(), adj[u].end(), par);
      if (it != adj[u].end()) {
        adj[u].erase(it);
      }
    }
    for (int v : adj[u]) {
      idx = build(v, u, idx);
      subtree[u] += subtree[v];
    }
    out[u] = idx++;
    return idx;
  }
};

struct lca_binary_jumping : rooted_tree {
  int L;
  vector<vector<int>> jump;
  lca_binary_jumping(const vector<vector<int>>& adj_list, int root):
    rooted_tree(adj_list, root) {
    build();
  }
  lca_binary_jumping(vector<vector<int>>&& adj_list, int root):
    rooted_tree(move(adj_list), root) {
    build();
  }

  int lca(int a, int b) const {
    if (depth[a] < depth[b]) std::swap(a, b);
    for (int j = L - 1; j >= 0; j--) {
      if (depth[a] - (1 << j) >= depth[b]) {
        a = jump[j][a];
      }
    }
    if (a == b) return a;
    for (int j = L - 1; j >= 0; j--) {
      if (jump[j][a] != jump[j][b]) {
        a = jump[j][a];
        b = jump[j][b];
      }
    }
    return parent[a];
  }
  int distance(int a, int b) const {
    return depth[a] + depth[b] - 2 * depth[lca(a, b)];
  }

private:
  void build() {
    L = 32 - __builtin_clz((int)adj.size());
    jump.resize(L, vector<int>(adj.size(), -1));
    for (int u : preorder) {
      jump[0][u] = parent[u];
      for (int j = 1; j < L && jump[j-1][u] != -1; j++) {
        jump[j][u] = jump[j-1][jump[j-1][u]];
      }
    }
  }
};

struct compressed_tree {
  vector<int> vertices;
  map<int, int> remap;
  vector<vector<pair<int, int>>> adj;
  vector<pair<int, int>> parent;
  compressed_tree(const lca_binary_jumping& tree, const vector<int>& verts) {
    if (verts.empty()) throw invalid_argument("vertices of compressed tree must not be empty");
    vector<pair<int, int>> order;
    for (int v : verts) {
      order.emplace_back(tree.in[v], v);
    }
    sort(begin(order), end(order));
    for (size_t i = 1; i < verts.size(); i++) {
      int lca = tree.lca(order[i].second, order[i - 1].second);
      order.emplace_back(tree.in[lca], lca);
    }
    sort(begin(order), end(order));

    int n = (int)(unique(begin(order), end(order)) - begin(order));
    vertices.resize(n);
    adj.resize(n);
    parent.resize(n);
    parent[0] = pair(-1, 0);
    for (int i = 0; i < n; i++) {
      vertices[i] = order[i].second;
      remap[vertices[i]] = i;
      if (i > 0) {
        int lca = tree.lca(vertices[i], vertices[i - 1]);
        int dist = tree.distance(vertices[i], lca);
        parent[i] = pair(remap[lca], dist);
        adj[parent[i].first].emplace_back(i, dist);
      }
    }
  }
  const vector<pair<int, int>>& operator [] (int i) const { return adj[i]; }
};

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
template <typename T>
using ordered_set = __gnu_pbds::tree<T,
      __gnu_pbds::null_type,
      less<T>,
      __gnu_pbds::rb_tree_tag,
      __gnu_pbds::tree_order_statistics_node_update>;

using ll = long long;
using ld = long double;
using pt = complex<ld>;

constexpr char nl = '\n';
constexpr int INF = 0x3f3f3f3f;
constexpr ll INFLL = 0x3f3f3f3f3f3f3f3f;
constexpr int MOD = 998244353;
constexpr ld EPS = 1e-9L;
random_device _rd; mt19937 rng(_rd());

tuple<ll, map<int, int>, int> solve(const compressed_tree& tree, const vector<bool>& mark, int u, int want) {
  ll res = 0;
  map<int, int> dists;
  if (mark[u]) dists[0] = 1;
  int offset = 0;
  for (auto [v, c] : tree[u]) {
    auto [add, subdists, suboff] = solve(tree, mark, v, want);
    res += add;
    suboff += c;
    if (size(dists) < size(subdists)) {
      swap(dists, subdists);
      swap(offset, suboff);
    }
    for (auto [d, cnt] : subdists) {
      int actual = d + suboff; // actual distance
      if (dists.count(want - actual - offset)) {
        res += (ll)dists[want - actual - offset] * cnt;
      }
    }
    for (auto [d, cnt] : subdists) {
      dists[d + suboff - offset] += cnt;
    }
  }
  return tuple(res, move(dists), offset);
}

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cout << fixed << setprecision(10);
#if defined(ONLINE_JUDGE) && defined(FILENAME)
  freopen(FILENAME ".in", "r", stdin);
  freopen(FILENAME ".out", "w", stdout);
#endif

  int T;
  cin >> T;
  while(T--) {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> adj(n+1);
    for(int i=1; i<n; i++) {
      int a, b;
      cin >> a >> b;
      adj[a].push_back(b);
      adj[b].push_back(a);
    }
    lca_binary_jumping tree(move(adj), 1);

    while(m--) {
      int k, d;
      cin >> k >> d;
      vector<int> verts(k);
      for(int i=0; i<k; i++) {
        cin >> verts[i];
      }
      compressed_tree graph(tree, verts);
      vector<bool> mark(graph.adj.size());
      for(int v : verts) {
        mark[graph.remap[v]] = true;
      }
      cout << get<0>(solve(graph, mark, 0, d)) << nl;
    }
  }

  return 0;
}

Recent Posts

See All

Commentaires


bottom of page