CF487E Tourists

cxqghzj·2023-09-19 15:31·6 次阅读

CF487E Tourists

题意#

给定一张 n 个点 m 条边的无向图,每个点有一个权值,维护下列两种操作。

C a w:将 a 点的权值改为 w

A a b:询问 ab 的任意路径的最小权值。

Sol#

两种操作在树上会很好维护,可以想到先建出圆方树。

对于每个方点,需要维护与她相邻的所有节点的权值。

不难想到,这里可以使用叶子节点是 multiset 的线段树维护。

这样建树的时候只会枚举边数,复杂度可以接受。

但是查询\修改时对于方点的每个相邻节点枚举,显然时间会炸。

考虑如何将贡献拆开。

image

我们考虑这样一张图。

假如我们对 2 号圆点做修改操作,那么她对 134 号方点都有贡献。

我们先将 1 号方点修改,由于节点的 father 只有一个,复杂度正确性显然。

image

思考在何时 3,4 号方点对答案有贡献,且 2 号圆点对答案没有直接贡献。

换句话说,何时答案的路径只经过 3,4 号点,而不经过 2 号点。

显然当 3,4 号点作为答案路径的 lca 时,成立。

image

所以我们在查询时,判断当前的 lca 是否为方点。

若是方点,便将当前点的 father 加入贡献。

复杂度为 O(nlogn)

Code#

Copy
#include <iostream> #include <algorithm> #include <cstdio> #include <array> #include <stack> #include <vector> #include <set> #define pii pair <int, int> using namespace std; #ifdef ONLINE_JUDGE /* #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) */ /* char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf; */ #endif int read() { int p = 0, flg = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') flg = -1; c = getchar(); } while (c >= '0' && c <= '9') { p = p * 10 + c - '0'; c = getchar(); } return p * flg; } void write(int x) { if (x < 0) { x = -x; putchar('-'); } if (x > 9) { write(x / 10); } putchar(x % 10 + '0'); } #define fi first #define se second const int N = 2e5 + 5, M = 4e5 + 5, inf = 2e9; namespace G { array <int, N> fir; array <int, M> nex, to; int cnt; void add(int x, int y) { cnt++; nex[cnt] = fir[x]; to[cnt] = y; fir[x] = cnt; } } namespace Rst { array <int, N> fir; array <int, M> nex, to; int cnt; void add(int x, int y) { cnt++; nex[cnt] = fir[x]; to[cnt] = y; fir[x] = cnt; } array <int, N> dfn, low; stack <int> stk; array <vector <int>, N> cur; vector <pii> edge; int tim, scc; void tarjan(int x) { tim++; dfn[x] = low[x] = tim; stk.push(x); for (int i = G::fir[x]; i; i = G::nex[i]) { if (!dfn[G::to[i]]) { tarjan(G::to[i]); low[x] = min(low[x], low[G::to[i]]); if (low[G::to[i]] < dfn[x]) continue; scc++; while (stk.top() != G::to[i]) { edge.push_back(make_pair(stk.top(), scc)); edge.push_back(make_pair(scc, stk.top())); cur[scc].push_back(stk.top()); stk.pop(); } edge.push_back(make_pair(stk.top(), scc)); edge.push_back(make_pair(scc, stk.top())); cur[scc].push_back(stk.top()); stk.pop(); edge.push_back(make_pair(x, scc)); edge.push_back(make_pair(scc, x)); cur[scc].push_back(x); } else low[x] = min(low[x], dfn[G::to[i]]); } } void init() { for (auto x : edge) add(x.fi, x.se); } } array <int, N> s; namespace Hpd { using Rst::fir, Rst::nex, Rst::to; array <int, N> dep, siz, son, fa; void dfs1(int x) { siz[x] = 1; for (int i = fir[x]; i; i = nex[i]) { if (to[i] == fa[x]) continue; fa[to[i]] = x; dep[to[i]] = dep[x] + 1; dfs1(to[i]); siz[x] += siz[to[i]]; if (siz[to[i]] > siz[son[x]]) son[x] = to[i]; } } array <int, N> dfn, idx, top; int cnt; void dfs2(int x, int Mgn) { cnt++; dfn[x] = cnt; idx[cnt] = x; top[x] = Mgn; if (son[x]) dfs2(son[x], Mgn); for (int i = fir[x]; i; i = nex[i]) { if (to[i] == son[x] || to[i] == fa[x]) continue; dfs2(to[i], to[i]); } } namespace Sgt { array <int, N * 4> edge; array <multiset <int>, N> leaf; using Rst::cur; void build(int x, int l, int r) { edge[x] = inf; if (l == r) { leaf[l].insert(inf); for (auto x : cur[idx[l]]) { if (x == fa[idx[l]]) continue; leaf[l].insert(s[x]); } if (!cur[idx[l]].size()) leaf[l].insert(s[idx[l]]); edge[x] = *leaf[l].begin(); return; } int mid = (l + r) >> 1; build(x * 2, l, mid); build(x * 2 + 1, mid + 1, r); edge[x] = min(edge[x * 2], edge[x * 2 + 1]); } void modify(int x, int l, int r, int k, pii y) { if (l > k || r < k) return; if (l == r) { leaf[l].erase(y.fi); leaf[l].insert(y.se); edge[x] = *leaf[l].begin(); return; } int mid = (l + r) >> 1; if (k <= mid) modify(x * 2, l, mid, k, y); else modify(x * 2 + 1, mid + 1, r, k, y); edge[x] = min(edge[x * 2], edge[x * 2 + 1]); } int query(int x, int l, int r, int L, int R) { if (L > r || R < l) return inf; if (L <= l && R >= r) return edge[x]; int mid = (l + r) >> 1, ans = inf; if (L <= mid) ans = min(ans, query(x * 2, l, mid, L, R)); if (R > mid) ans = min(ans, query(x * 2 + 1, mid + 1, r, L, R)); return ans; } } using Rst::scc; void modify(int x, int y) { Sgt::modify(1, 1, scc, dfn[fa[x]], make_pair(s[x], y)); Sgt::modify(1, 1, scc, dfn[x], make_pair(s[x], y)); s[x] = y; } int query(int x, int y) { int ans = inf; while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x, y); ans = min(ans, Sgt::query(1, 1, scc, dfn[top[x]], dfn[x])); x = fa[top[x]]; } if (dfn[x] > dfn[y]) swap(x, y); ans = min(ans, Sgt::query(1, 1, scc, dfn[x], dfn[y])); if (Rst::cur[x].size()) ans = min(ans, s[fa[x]]); return ans; } } char strbuf[2]; int main() { int n = read(), m = read(), q = read(); for (int i = 1; i <= n; i++) s[i] = read(); s[0] = inf; for (int i = 1; i <= m; i++) { int x = read(), y = read(); G::add(x, y), G::add(y, x); } Rst::scc = n; Rst::tarjan(1), Rst::init(); Hpd::dfs1(1), Hpd::dfs2(1, 0); Hpd::Sgt::build(1, 1, Rst::scc); while (q--) { scanf("%s", strbuf); int x = read(), y = read(); if (strbuf[0] == 'C') Hpd::modify(x, y); else write(Hpd::query(x, y)), puts(""); } return 0; }
posted @   cxqghzj  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示
目录