CF 487E Tourists
题意
给定一张简单无向连通图,要求支持两种操作:
- 修改一个点的点权。
- 询问两点之间所有简单路径上点权的最小值。
题解
一道比较模板的圆方树题,先建出原图的圆方树,这样问题就转化为了树上的路径问题。
如果我们令方点的权值为相邻圆点的最小值,查询只需要查询路径最小值,只需要树链剖分维护即可。但是这样有一个问题,修改一个圆点权值的时候可能会同时修改 \(O(n)\) 个方点,无法承受。
一个巧妙的做法是,我们令方点的权值为儿子权值的最小值,这样修改的时候只会修改父亲的权值,直接对每个点开 multiset 维护。查询的时候注意如果 lca 是圆点,需要额外查询 lca 父亲的权值。
#include <bits/stdc++.h>
const int N = 2e5 + 10, M = 4e5 + 10, inf = 2147483647;
inline int read()
{
int x = 0;
char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return x;
}
std::stack<int> stk;
std::vector<int> G[N];
std::multiset<int> S[N];
int cnt = 0, idx = 0, dfn[N], low[N];
int tot = 1, fir[N], nex[M], got[M];
int Idx = 0, siz[N], son[N], dep[N], par[N], top[N], rev[N], w[N];
struct SegmentTree
{
int Min[N << 2];
#define mid ((l + r) >> 1)
inline void upd(int k)
{
Min[k] = std::min(Min[k << 1], Min[k << 1 | 1]);
}
inline void build(int k, int l, int r)
{
if (l == r) return Min[k] = w[rev[l]], void();
build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r), upd(k);
}
inline void Mod(int k, int l, int r, int p, int dat)
{
if (l == r) return Min[k] = dat, void();
p <= mid ? Mod(k << 1, l, mid, p, dat) : Mod(k << 1 | 1, mid + 1, r, p, dat); upd(k);
}
inline int Ask(int k, int l, int r, int ql, int qr)
{
if (qr < l || r < ql) return inf;
if (ql <= l && r <= qr) return Min[k];
return std::min(Ask(k << 1, l, mid, ql, qr), Ask(k << 1 | 1, mid + 1, r, ql, qr));
}
}T;
inline void AddEdge(int u, int v)
{
// printf("%d %d\n", u, v);
nex[++tot] = fir[u], fir[u] = tot, got[tot] = v;
nex[++tot] = fir[v], fir[v] = tot, got[tot] = u;
}
inline void dfs(int u)
{
dfn[u] = low[u] = ++idx, stk.push(u);
for (int v: G[u]) if (!dfn[v])
{
dfs(v), low[u] = std::min(low[u], low[v]);
if (low[v] == dfn[u])
{
++cnt;
for (int x = 0; x != v; stk.pop())
x = stk.top(), AddEdge(x, cnt);
AddEdge(u, cnt);
}
}
else low[u] = std::min(low[u], dfn[v]);
}
inline void dfs(int u, int fa)
{
dep[u] = dep[par[u] = fa] + 1, siz[u] = 1;
for (int i = fir[u]; i; i = nex[i]) if (got[i] != fa)
{
dfs(got[i], u), siz[u] += siz[got[i]];
if (siz[got[i]] > siz[son[u]]) son[u] = got[i];
}
}
inline void Dfs(int u, int fa)
{
dfn[u] = ++Idx, rev[Idx] = u;
top[u] = u == son[fa] ? top[fa] : u;
if (son[u]) Dfs(son[u], u);
for (int i = fir[u]; i; i = nex[i])
if (got[i] != fa && got[i] != son[u]) Dfs(got[i], u);
}
int main()
{
int n = read(), m = read(), q = read();
for (int i = 1; i <= n; ++i) w[i] = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read();
G[u].push_back(v), G[v].push_back(u);
}
cnt = n, dfs(1), dfs(1, 0), Dfs(1, 0);
for (int i = 2; i <= n; ++i) S[par[i]].insert(w[i]);
for (int i = n + 1; i <= cnt; ++i) w[i] = *S[i].begin();
T.build(1, 1, cnt);
for (int i = 1; i <= q; ++i)
{
int opt = getchar();
while (opt != 'C' && opt != 'A') opt = getchar();
if (opt == 'C')
{
int u = read(), dat = read();
T.Mod(1, 1, cnt, dfn[u], dat);
if (par[u])
{
S[par[u]].erase(S[par[u]].lower_bound(w[u])), S[par[u]].insert(dat);
if (w[par[u]] != *S[par[u]].begin())
T.Mod(1, 1, cnt, dfn[par[u]], w[par[u]] = *S[par[u]].begin());
}
w[u] = dat;
}
else
{
int u = read(), v = read(), ans = inf;
for (int fu = top[u], fv = top[v]; fu != fv; fu = top[u = par[fu]])
{
if (dep[fu] < dep[fv]) std::swap(fu, fv), std::swap(u, v);
ans = std::min(ans, T.Ask(1, 1, cnt, dfn[fu], dfn[u]));
}
if (dep[u] > dep[v]) std::swap(u, v);
ans = std::min(ans, T.Ask(1, 1, cnt, dfn[u], dfn[v]));
printf("%d\n", std::min(ans, u > n ? w[par[u]] : inf));
}
}
return 0;
}

浙公网安备 33010602011771号