CF 487ETourists(JZOJ4691旅行) 树链剖分维护点双连通分量信息

    xiaoxiao2025-08-01  5

    题目大意

    给定一个 N 个点M条边的无向图,每个点有一个点权。现在有两种操作,第一种是修改一个点的点权,第二种是询问两点间路径上的最小点权(不能经过重复的点)。操作数为 Q

    N,M,Q109

    解题思路

    我们先考虑没有修改点权的情况。那么我们可以先用点双连通分量缩环,缩环后的点权为环上的最小值。而每组询问就变成了求树上两点间的最权值。这个经典问题可以用倍增后链剖来维护。

    但是有修改点权时这个方法就行不通了,因为一个点可能存在与多个联通块,特别是菊花图的情况复杂度就会到 N2 级别。那么我们考虑另一种建树的方式,对于一个联通块,我们需要特殊考虑的就只有割点,那么我们可这样建树:对于每个点双我们新增加一个节点,里面存的是这个点双中最小的权值,对于在Dfs序不是最小的点都向这个点连一条边。而对于Dfs序最小的点,我们让新增的点向它连一条边(姑且称为特殊点)。而我们修改一个点时,把代表它所在点双的新增点也更新。而现在的问题是我们对于一个割点,不能修改所有它所处的点双,那么我们讨论一下这些点上与它在树上的关系: 1. 他的父亲:因为父亲只有一个,并且一定是代表一个点双的点,那么直接把他的权值更新就好了。 2. 他的儿子:对于它的儿子可能有很多,我们不能一一去修改,但是我们可以发现,对于每次询问到这个点儿子代表的点双时只有两种情况,一个就是 Lca 在这个这个点双代表点的上方,那么也肯定会经过我们修改修改的点。否则 Lca 肯定就是这个点双的代表点,那么除了查询路径上的值,再查询一下这和点双代表点的父亲就可以了。

    这样的话每次修改就只用修改两个点,每次查询最多只会查询一条路径加上一个点,那么复杂度就变成了 OQlog2N 。注意由于有了修改操作所以不能用倍增维护。

    程序

    //YxuanwKeith #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #include <set> using namespace std; const int MAXN = 2e5 + 5; const int Inf = 1e9 + 7; multiset<int> S[MAXN]; vector<int> R[MAXN]; int N, M, Q, Val[MAXN], Tr[MAXN * 4]; int tot, Last[MAXN], Next[MAXN * 2], Go[MAXN * 2]; int top, time, Cnt, Low[MAXN], Dfn[MAXN], D[MAXN], Bel[MAXN]; int Deep[MAXN], Pre[MAXN], Size[MAXN], MSon[MAXN], Top[MAXN], Ord[MAXN]; void Link(int u, int v) { Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v; } void Tarjan(int Now, int Pre) { Dfn[Now] = Low[Now] = ++ time; D[++ top] = Now; for (int i = 0; i < R[Now].size(); i ++) { int v = R[Now][i]; if (!Dfn[v]) { Tarjan(v, Now); Low[Now] = min(Low[Now], Low[v]); if (Low[v] >= Dfn[Now]) { ++ Cnt; while (1 == 1) { int t = D[top --]; Bel[t] = Cnt; S[Cnt].insert(Val[t]); Link(Cnt, t); if (t == v) break; } Link(Now, Cnt); } } else if (v != Pre) Low[Now] = min(Low[Now], Dfn[v]); } } void Modify(int Now, int l, int r, int Side, int Val) { if (l == r) { Tr[Now] = Val; return; } int Mid = (l + r) >> 1; if (Side <= Mid) Modify(Now * 2, l, Mid, Side, Val); else Modify(Now * 2 + 1, Mid + 1, r, Side, Val); Tr[Now] = min(Tr[Now * 2], Tr[Now * 2 + 1]); } void Modify(int Side, int V) { if (Bel[Side]) { int Now = Bel[Side]; S[Now].erase(S[Now].find(Val[Side])); S[Now].insert(V); Modify(1, 1, time, Ord[Now], *S[Now].begin()); } Val[Side] = V; Modify(1, 1, time, Ord[Side], V); } int Query(int Now, int l, int r, int lx, int rx) { if (l == lx && r == rx) return Tr[Now]; int Mid = (l + r) >> 1; if (rx <= Mid) return Query(Now * 2, l, Mid, lx, rx); else if (lx > Mid) return Query(Now * 2 + 1, Mid + 1, r, lx, rx); else return min(Query(Now * 2, l, Mid, lx, Mid), Query(Now * 2 + 1, Mid + 1, r, Mid + 1, rx)); } int Query(int u, int v) { int Num = Inf; while (Top[u] != Top[v]) { if (Deep[Top[u]] < Deep[Top[v]]) swap(u, v); Num = min(Num, Query(1, 1, time, Ord[Top[u]], Ord[u])); u = Pre[Top[u]]; } if (Deep[u] < Deep[v]) swap(u, v); Num = min(Num, Query(1, 1, time, Ord[v], Ord[u])); if (v > N && Pre[v]) Num = min(Num, Val[Pre[v]]); return Num; } void Basis(int Now, int Fa) { Size[Now] = 1, Pre[Now] = Fa, Deep[Now] = Deep[Fa] + 1; for (int p = Last[Now]; p; p = Next[p]) { int v = Go[p]; if (v == Fa) continue; Basis(v, Now); Size[Now] += Size[v]; if (Size[v] > Size[MSon[Now]]) MSon[Now] = v; } } void Promote(int Now, int top) { if (!Now) return; Ord[Now] = ++ time, Top[Now] = top; Promote(MSon[Now], top); for (int p = Last[Now]; p; p = Next[p]) { int v = Go[p]; if (v == MSon[Now] || v == Pre[Now]) continue; Promote(v, v); } } void Prepare() { Cnt = N; Tarjan(1, 0); time = 0; Basis(1, 0), Promote(1, 1); for (int i = 1; i <= time; i ++) { if (i <= N) Modify(1, 1, time, Ord[i], Val[i]); else Modify(1, 1, time, Ord[i], *S[i].begin()); } } void Solve() { for (int i = 1; i <= Q; i ++) { char ch; scanf("\n%c", &ch); if (ch == 'C') { int Side, Val; scanf("%d%d", &Side, &Val); Modify(Side, Val); } else { int u, v; scanf("%d%d", &u, &v); printf("%d\n", Query(u, v)); } } } int main() { scanf("%d%d%d", &N, &M, &Q); for (int i = 1; i <= N; i ++) scanf("%d", &Val[i]); for (int i = 1; i <= M; i ++) { int u, v; scanf("%d%d", &u, &v); R[u].push_back(v); R[v].push_back(u); } Prepare(); Solve(); }
    转载请注明原文地址: https://ju.6miu.com/read-1301299.html
    最新回复(0)