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:
- Edging; Delete edge
- 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