LOJ #3540. "JOI Open 2018" landslide

Posted by nullified on Mon, 24 Jan 2022 10:45:08 +0100

LOJ #3540. "JOI Open 2018" landslide

​ There is an undirected graph \ (G \), and for each polar pass block \ (T \) of \ (G \), find its arbitrary spanning tree \ (T '\), then the number of polar pass blocks of graph \ (G \) is \ (n - \ sum|e {T'}| \), where \ (E_G \) is the edge set of graph \ (G \). Therefore, we only need to find \ (\ sum|e {T '}| \).

​ Note that the graph \ (G '\) after the query \ ((W,P) \) does not have an edge \ ((u,v) \) that satisfies \ (U \ Le, P \) and \ (P < V \). So we can divide it into two parts for statistics: the polar Dalian pass block composed of the first statistical point \ (0\sim P \) and the polar Dalian pass block composed of the second statistical point \ (P+1\sim n-1 \). Since the two statistics are essentially the same, we might as well think about how to realize the first statistics.

​ Set the weight of each edge \ (e(u,v) \) to \ (w_, e = \ Max \ {u, V \} \). For a certain \ (P \), it is easy to find that there is no edge \ (E \) of any weight \ (w_e > P \) in the graph \ (G '\). Considering the gradual increase of \ (P \), the edges in the graph \ (G '\) become more and more, and the number of polar blocks decreases gradually. Therefore, we need to know when each pole Dalian pass block exists. For a pole pass block \ (T \), let its minimum spanning tree be \ (T '{\ min} \), then when \ (P \ Ge \ Max \ {e {T' {\ min}} \} \), the pole pass block exists. So we can consider the minimum spanning tree forest of the current graph \ (g \).

​ Let the currently added edge set be \ (E \), and the minimum spanning tree forest of graph \ (g \) be \ (g {\ min} \). We find that for one side of \ (E \), if \ (e\notin {G_\min} \), then \ (E \) does not contribute; If \ (e\in G_\min \), when \ (P\ge w_e \), it will contribute \ (1 \) to \ (\ sum {_e {g_ \ min}|} \). Therefore, for a certain \ (P \), the contribution of the edge set \ (E \) to the answer is \ (\ sum \ limits {e \ in G \ min} [\, w _e \ Le P \,] \). What we want to do now is:

  1. Edging; Delete edge
  2. Ask how many edges in the forest of the current minimum spanning tree have the weight \ (\ le P \).

We can think of using \ (\ text{LCT} \) to maintain the minimum spanning tree forest. However, the troublesome point is that \ (\ text{LCT} \) maintaining the minimum spanning tree does not support deleting any edge. Therefore, we can use the segment tree divide and conquer to change the edge deletion operation into rollback operation, and then we can vigorously \ (\ text{LCT} \) maintain it. The total time complexity is \ (\ mathcal O(n\log ^2n) \), one \ (\ log \) is the segment tree divide and conquer, and the other \ (\ log \) is \ (\ text{LCT} \).

Reference code
#include <bits/stdc++.h>
#include "collapse.h"
using namespace std;
static constexpr int Maxn = 2e5 + 5;
int n, m, q, cn;
vector<int> ans;

int val[Maxn];
inline bool val_cmp(int x, int y) { return val[x] > val[y]; }
namespace lct {
  static constexpr int MaxN = 2e5 + 5;
  bool rev[MaxN];
  int fa[MaxN], ch[MaxN][2];
  int tr[MaxN];
#define nroot(x) ((ch[fa[x]][0] == x) || (ch[fa[x]][1] == x))
#define getdir(x) (ch[fa[x]][1] == x)
  inline void pushrev(int p) { swap(ch[p][0], ch[p][1]), rev[p] ^= 1; }
  void pushdown(int p) {
    if (rev[p]) {
      if (ch[p][0]) pushrev(ch[p][0]);
      if (ch[p][1]) pushrev(ch[p][1]);
      rev[p] = false;
    }
  } // lct::pushdown
  void pushup(int p) {
    tr[p] = p;
    if (ch[p][0]) tr[p] = min(tr[p], tr[ch[p][0]], val_cmp);
    if (ch[p][1]) tr[p] = min(tr[p], tr[ch[p][1]], val_cmp);
  } // lct::pushup
  void rotate(int p) {
    int par = fa[p], grd = fa[par];
    bool dir = getdir(p);
    ch[par][dir] = ch[p][dir ^ 1];
    fa[ch[p][dir ^ 1]] = par; fa[p] = grd;
    if (nroot(par)) ch[grd][getdir(par)] = p;
    fa[par] = p, ch[p][dir ^ 1] = par;
    pushup(par), pushup(p);
  } // lct::rotate
  void splay(int p) {
    static int stk[MaxN], top; stk[top = 1] = p;
    for (int t = p; nroot(t); ) stk[++top] = t = fa[t];
    while (top) pushdown(stk[top--]);
    for (int par = fa[p]; nroot(p); rotate(p), par = fa[p])
      if (nroot(par)) rotate(getdir(p) == getdir(par) ? par : p);
  } // lct::splay
  void access(int p) {
    for (int x = 0; p; x = p, p = fa[p])
      splay(p), ch[p][1] = x, pushup(p);
  } // lct::access
  void make_root(int p) {
    access(p), splay(p);
    pushrev(p);
  } // lct::make_root
  int find_root(int p) {
    access(p), splay(p);
    while (ch[p][0]) pushdown(p), p = ch[p][0];
    splay(p);
    return p;
  } // lct::find_root
  void link(int x, int y) {
    make_root(x);
    fa[x] = y;
  } // lct::link
  void cut(int x, int y) {
    make_root(x);
    access(y), splay(y);
    assert(fa[x] == y && ch[y][0] == x);
    fa[x] = ch[y][0] = 0;
    pushup(y);
  } // lct::cut
} // namespace lct

namespace fen {
  static constexpr int MaxN = 2e5 + 5;
  int bit[Maxn];
  void upd(int x, int v) {
    for (; x <= n; x += x & -x) bit[x] += v;
  } // fen::upd
  int ask(int x) {
    int r = 0;
    for (; x; x -= x & -x) r += bit[x];
    return r;
  } // fen::ask
} // namespace fen

vector<pair<int, int>> qs[Maxn];
pair<int, int> e[Maxn];
vector<int> es[Maxn * 4];
void link_to(int p, int l, int r, int L, int R, int x) {
  if (L >= r || l >= R) return ;
  if (L <= l && r <= R) return es[p].push_back(x);
  int mid = (l + r) / 2;
  link_to(p * 2 + 0, l, mid, L, R, x);
  link_to(p * 2 + 1, mid, r, L, R, x);
} // link_to
int stk[Maxn], top;
bool added[Maxn];
void del_edge(int i, bool Stack) {
  int x = e[i].first, y = e[i].second;
  lct::cut(i, x), lct::cut(i, y);
  if (Stack) stk[++top] = i, added[top] = false;
  fen::upd(val[i], -1);
} // del_edge
void add_edge(int i, bool Stack) {
  int x = e[i].first, y = e[i].second;
  lct::make_root(x);
  if (Stack && lct::find_root(y) == x) {
    if (val[lct::tr[x]] > val[i])
      del_edge(lct::tr[x], true);
    else return;
  }
  lct::link(i, x), lct::link(i, y);
  if (Stack) stk[++top] = i, added[top] = true;
  fen::upd(val[i], 1);
} // add_edge
void divide(int p, int l, int r) {
  int rec_top = top;
  for (const int &i: es[p]) add_edge(i, true);
  if (l + 1 == r) {
    for (const auto &[x, i]: qs[l])
      ans[i] += fen::ask(x);
  } else {
    int mid = (l + r) / 2;
    divide(p * 2 + 0, l, mid);
    divide(p * 2 + 1, mid, r);
  }
  for (; top > rec_top; --top) {
    if (added[top] == true)
      del_edge(stk[top], false);
    else add_edge(stk[top], false);
  }
} // divide

std::vector<int> simulateCollapse(
	int N,
	std::vector<int> T,
	std::vector<int> X,
	std::vector<int> Y,
	std::vector<int> W,
	std::vector<int> P
) {

  n = N, m = (int)T.size(), q = (int)W.size();
  ans.assign(q, 0);
  for (int &x: X) ++x;
  for (int &x: Y) ++x;
  for (int &x: P) ++x;
  for (int i = 0; i < m; ++i)
    if (X[i] > Y[i]) swap(X[i], Y[i]);
  map<pair<int, int>, int> ep;
  cn = n;
  for (int i = 0; i < m; ++i) {
    if (T[i] == 0) {
      ep[{X[i], Y[i]}] = i;
    } else {
      int &t = ep[{X[i], Y[i]}];
      e[++cn] = {X[i], Y[i]};
      link_to(1, 0, m, t, i, cn);
      t = -1;
    }
  }
  for (auto &[E, t]: ep)
    if (t != -1) {
      e[++cn] = E;
      link_to(1, 0, m, t, m, cn);
      t = -1;
    }
  for (int i = 0; i < q; ++i)
    qs[W[i]].push_back({P[i], i});

  for (int i = 1; i <= cn; ++i) lct::tr[i] = i;
#define calculate {                                             \
  for (int i = n + 1; i <= cn; ++i) val[i] = e[i].second;       \
  top = 0, divide(1, 0, m);                                     \
}
  calculate
  for (int i = n + 1; i <= cn; ++i) {
    e[i].first = n - e[i].first + 1;
    e[i].second = n - e[i].second + 1;
    swap(e[i].first, e[i].second);
  }
  for (int t = 0; t < m; ++t)
    for (auto &[x, id]: qs[t]) x = n - x;
  calculate

  for (int &x: ans) x = n - x;
  return ans;
} // simulateCollapse