【题目链接】
【思路要点】
- 首先我们来证明点双连通分量的一个性质。
- 引理:在一个点双连通分量中,给定任意三个不同的点\(a\),\(b\),\(c\),一定存在一条从\(a\)到\(c\)的,经过每个点至多一次的简单路径经过了\(b\)。
- 证明:考虑网络流。在原图中存在无向边的点对之间建立无向边,容量为1;对每个点拆点限流,容量为1;由\(a\)和\(c\)向汇点连边,容量为1;由源点向\(b\)连边,容量为2。显然,原命题等价于源点到汇点的最大流等于2。显然源点到汇点的最大流不超过2,现在我们来证明它大于1。考虑最小割,我们发现割去一条点对之间的无向边等价于删去原图中的一条边,割去一条限流边等价于删去原图中的一个点,由于原图点双连通,我们无法只割去一条容量为1的边使得源点到汇点不连通,因此最小割大于1,原命题得证。
- 那么,我们建立圆方树,定义圆点的权值为其本身的权值,方点的权值为其内部所有圆点权值的最小值,这一点可以使用一个堆来维护。
- 询问就等价于询问树上两点之间权值的最小值,可以通过树链剖分+线段树实现。
- 但我们发现修改一个圆点可能会导致\(O(N)\)个方点的权值发生变化,因此复杂度有可能会退化。
- 考虑圆方树上两个圆点之间的路径,令它们的Lca为\(x\)。
- 若\(x\)为一个圆点,那么路径上经过的每一个方点的父亲圆点也均被经过了。
- 若\(x\)为一个方点,那么路径上经过的每一个不是\(x\)的方点的父亲圆点也均被经过了。
- 因此,我们将每一个圆点的权值附加在其唯一的父亲方点上,在询问时特判\(x\)为一个方点的情况即可。
- 时间复杂度\(O(N+M+QLog^2N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; const int MAXP = 400005; const int INF = 1e9; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct SegmentTree { struct Node { int lc, rc; int Min; } a[MAXP]; int root, n, size; void build(int &root, int l, int r) { root = ++size; a[root].Min = INF; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } void update(int root) { a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min); } void modify(int root, int l, int r, int pos, int x) { if (l == r) { a[root].Min = x; return; } int mid = (l + r) / 2; if (mid >= pos) modify(a[root].lc, l, mid, pos, x); else modify(a[root].rc, mid + 1, r, pos, x); update(root); } void modify(int pos, int x) { modify(root, 1, n, pos, x); } int query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Min; int ans = INF, mid = (l + r) / 2; if (mid >= ql) chkmin(ans, query(a[root].lc, l, mid, ql, min(qr, mid))); if (mid + 1 <= qr) chkmin(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } int query(int l, int r) { return query(root, 1, n, l, r); } } ST; struct Heap { priority_queue <int, vector<int>, greater<int> > Heap, Delt; void push(int x) {Heap.push(x); } void delt(int x) {Delt.push(x); } int query() { while (!Delt.empty() && Heap.top() == Delt.top()) { Heap.pop(); Delt.pop(); } return Heap.top(); } } Heap[MAXN]; int n, m, q, oldn, val[MAXN]; int top, Stack[MAXN]; int timer, dfn[MAXN], low[MAXN], up[MAXN]; int depth[MAXN], father[MAXN], size[MAXN], son[MAXN]; vector <int> a[MAXN], b[MAXN]; void dfs(int pos, int fa) { size[pos] = 1; father[pos] = fa; depth[pos] = depth[fa] + 1; for (unsigned i = 0; i < b[pos].size(); i++) if (b[pos][i] != fa) { dfs(b[pos][i], pos); size[pos] += size[b[pos][i]]; if (size[b[pos][i]] > size[son[pos]]) son[pos] = b[pos][i]; } } void efs(int pos, int fa) { dfn[pos] = ++timer; up[pos] = fa; if (pos <= oldn) ST.modify(timer, val[pos]); else ST.modify(timer, Heap[pos].query()); if (son[pos]) efs(son[pos], fa); for (unsigned i = 0; i < b[pos].size(); i++) if (b[pos][i] != father[pos] && b[pos][i] != son[pos]) efs(b[pos][i], b[pos][i]); } void tarjan(int pos) { Stack[++top] = pos; dfn[pos] = low[pos] = ++timer; for (unsigned i = 0; i < a[pos].size(); i++) if (dfn[a[pos][i]] == 0) { tarjan(a[pos][i]); chkmin(low[pos], low[a[pos][i]]); if (low[a[pos][i]] >= dfn[pos]) { int tmp = 0; n++; while (tmp != a[pos][i]) { tmp = Stack[top--]; Heap[n].push(val[tmp]); b[n].push_back(tmp); b[tmp].push_back(n); } b[n].push_back(pos); b[pos].push_back(n); } } else chkmin(low[pos], dfn[a[pos][i]]); } int query(int x, int y) { int ans = INF; while (up[x] != up[y]) { if (depth[up[x]] < depth[up[y]]) swap(x, y); chkmin(ans, ST.query(dfn[up[x]], dfn[x])); x = father[up[x]]; } if (dfn[x] > dfn[y]) swap(x, y); chkmin(ans, ST.query(dfn[x], dfn[y])); if (x > oldn) chkmin(ans, val[father[x]]); return ans; } int main() { read(n), read(m), oldn = n, read(q); for (int i = 1; i <= n; i++) read(val[i]); for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); a[x].push_back(y); a[y].push_back(x); } tarjan(1); timer = 0; memset(dfn, 0, sizeof(dfn)); dfs(1, 0); ST.init(n); efs(1, 1); for (int i = 1; i <= q; i++) { char opt; int x, y; scanf("\n%c%d%d", &opt, &x, &y); if (opt == 'C') { int old = val[x]; val[x] = y; ST.modify(dfn[x], y); if (father[x]) { int f = father[x]; Heap[f].delt(old); Heap[f].push(y); ST.modify(dfn[f], Heap[f].query()); } } else writeln(query(x, y)); } return 0; }