[题解] CF487E Tourists
前言
运用算法:点双连通分量,圆方树,树链剖分,splay(可以用 multiset 维护,不知道 OI 可不可以使用,所以就用了 splay )。
题目链接。
题目大意
有 \(n\) 个点, \(m\) 条边,每个点 \(i\) 都有点权 \(w_i\) 。有两个操作:
- C a w: 表示 a 点的权值变为 w。
- A a b: 求从 a 点到 b 点的一条路径上,使得每个点只出现一次,且最小权值最小的一条路径的最小值。
思路
先说明圆方树,经常用于求解与无向图。下见如何构造圆方树(来自 WC 的 PPT 的一张图):
先用 Tarjan 算法求出点双连通分量,原有的点成为圆点。对于每个点双连通分量,建立一个方点,将点双连通分量内的所有圆点都连向这个方点。
显然,有一条重要性质成立:每条边的两个端点,一个是圆点,一个是方点。
对于一个普通的无向图,要进行维护其点权,可以先将其变为圆方树,在进行维护,是一些题的基本思路。树相对于图有更好的性质,可以用树链剖分等来维护。
题目中要维护最小值,那么方点的权值就为所连圆点的最小值。最小值使用 splay 维护,最后按照要求进行维护即可。
修改一个圆点,就会改动其旁边所有的方点。考虑菊花图,会被卡成 \(O(nq)\) 。
稍微在上面做出改动,每个方点的权值为子节点的权值,暂时不考虑其父节点。树剖查询时,若两点的 LCA 为方点,最小值多比较一个父节点的权值即可。
时间复杂度 \(O(q\log^2n+n\log n)\)
Code
#include <stack>
#include <cstdio>
#include <cstring>
using namespace std;
namespace Quick_Function {
template <typename Temp> void Read(Temp &x) {
x = 0; char ch = getchar(); bool op = 0;
while(ch < '0' || ch > '9') { if(ch == '-') op = 1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
if(op) x = -x;
}
template <typename T, typename... Args> void Read(T &t, Args &... args) { Read(t); Read(args...); }
template <typename Temp> Temp Max(Temp x, Temp y) { return x > y ? x : y; }
template <typename Temp> Temp Min(Temp x, Temp y) { return x < y ? x : y; }
template <typename Temp> Temp Abs(Temp x) { return x < 0 ? (-x) : x; }
template <typename Temp> void Swap(Temp &x, Temp &y) { x ^= y ^= x ^= y; }
}
using namespace Quick_Function;
#define INF 0x3f3f3f3f
const int MAXN = 1e6 + 5;
struct edge1 { int To, Next; };
edge1 edge1[MAXN << 1], edge2[MAXN << 1];
int head1[MAXN], head2[MAXN];
int edgetot1, edgetot2;
void Addedge1(int u, int v) {
edge1[++edgetot1].Next = head1[u], edge1[edgetot1].To = v, head1[u] = edgetot1;
edge1[++edgetot1].Next = head1[v], edge1[edgetot1].To = u, head1[v] = edgetot1;
}
void Addedge2(int u, int v) {
edge2[++edgetot2].Next = head2[u], edge2[edgetot2].To = v, head2[u] = edgetot2;
edge2[++edgetot2].Next = head2[v], edge2[edgetot2].To = u, head2[v] = edgetot2;
}
int w[MAXN], n, m, q;
stack<int> stk;
int dfn1[MAXN], low[MAXN];
int tim1, cnt;
void Tarjan(int u) {
dfn1[u] = low[u] = ++tim1; stk.push(u);
for(int i = head1[u]; i; i = edge1[i].Next) {
int v = edge1[i].To;
if(!dfn1[v]) {
Tarjan(v);
low[u] = Min(low[u], low[v]);
if(low[v] >= dfn1[u]) {
int Top = -1; cnt++;
while(Top != v) {
Top = stk.top(); stk.pop();
Addedge2(Top, cnt);
}
Addedge2(u, cnt);
}
}
else low[u] = Min(low[u], dfn1[v]);
}
}
struct Segment_Node {
int Left, Right, MinData;
#define LS(x) (x << 1)
#define RS(x) (x << 1 | 1)
#define L(x) segment_tree[x].Left
#define R(x) segment_tree[x].Right
#define M(x) segment_tree[x].MinData
};
struct Segment_Tree {
Segment_Node segment_tree[MAXN << 2];
void Push_Up(int pos) { M(pos) = Min(M(LS(pos)), M(RS(pos))); }
void Build(int pos, int l, int r) {
L(pos) = l, R(pos) = r;
if(l == r) return;
int mid = (l + r) >> 1;
Build(LS(pos), l, mid), Build(RS(pos), mid + 1, r);
}
void Update(int pos, int x, int k) {
if(L(pos) == R(pos)) { M(pos) = k; return; }
if(x <= R(LS(pos))) Update(LS(pos), x, k);
else Update(RS(pos), x, k);
Push_Up(pos);
}
int Query_Min(int pos, int l, int r) {
if(l <= L(pos) && R(pos) <= r) return M(pos);
int res = INF;
if(l <= R(LS(pos))) res = Min(res, Query_Min(LS(pos), l, r));
if(r >= L(RS(pos))) res = Min(res, Query_Min(RS(pos), l, r));
return res;
}
};
Segment_Tree tree1;
struct Splay_Node {
int Child[2], Val, Cnt, Siz, Father, Belong;
#define Son(x, y) splay[x].Child[y]
#define V(x) splay[x].Val
#define C(x) splay[x].Cnt
#define S(x) splay[x].Siz
#define F(x) splay[x].Father
#define B(x) splay[x].Belong
};
struct Splay_Tree {
int root[MAXN], tot;
Splay_Node splay[MAXN * 3];
bool Ident(int pos) { return Son(F(pos), 1) == pos; }
int New(int val, int fa, int now) {
F(++tot) = fa;
C(tot) = S(tot) = 1;
V(tot) = val;
B(tot) = now;
return tot;
}
void Build() {
for(int i = 1; i <= cnt - n; i++) {
root[i] = New(-INF, 0, i);
Son(root[i], 1) = New(INF, root[i], i);
}
}
void Update(int pos) { S(pos) = C(pos) + S(Son(pos, 0)) + S(Son(pos, 1)); }
void Connect(int pos, int fa, int flag) {
Son(fa, flag) = pos;
F(pos) = fa;
}
void Rotate(int pos) {
int fa = F(pos), grand = F(fa);
int flag1 = Ident(pos), flag2 = Ident(fa);
Connect(Son(pos, flag1 ^ 1), fa, flag1);
Connect(fa, pos, flag1 ^ 1);
Connect(pos, grand, flag2);
Update(fa);
Update(pos);
}
void Splay(int pos, int to) {
for(int fa = F(pos); F(pos) != to; Rotate(pos), fa = F(pos))
if(F(fa) != to) Ident(pos) == Ident(fa) ? Rotate(fa) : Rotate(pos);
if(!to) root[B(pos)] = pos;
}
void Insert(int &pos, int val, int fa) {
if(!pos) {
pos = New(val, fa, B(fa));
Splay(pos, 0);
return;
}
if(val == V(pos)) {
C(pos)++;
Splay(pos, 0);
return;
}
else if(val < V(pos)) Insert(Son(pos, 0), val, pos);
else Insert(Son(pos, 1), val, pos);
}
void Remove(int pos, int val) {
if(!pos) return;
if(val == V(pos)) {
if(C(pos) > 1) {
C(pos)--;
Splay(pos, 0);
return;
}
if(Son(pos, 0)) Rotate(Son(pos, 0)), Remove(pos, val);
else if(Son(pos, 1)) Rotate(Son(pos, 1)), Remove(pos, val);
else {
int newroot = F(pos);
Son(F(pos), Ident(pos)) = 0;
Splay(newroot, 0);
}
return;
}
else if(val < V(pos)) Remove(Son(pos, 0), val);
else Remove(Son(pos, 1), val);
}
int Query_Min(int pos) {
if(!pos) return INF;
if(S(Son(pos, 0)) >= 2) return Query_Min(Son(pos, 0));
Splay(pos, 0); return V(pos);
}
};
Splay_Tree tree2;
int dfn2[MAXN], siz[MAXN], dep[MAXN], son[MAXN], fa[MAXN], tp[MAXN];
int tim2;
void dfs1(int u, int pre) {
dep[u] = dep[pre] + 1, fa[u] = pre, siz[u] = 1;
int maxn = -INF;
for(int i = head2[u]; i; i = edge2[i].Next) {
int v = edge2[i].To;
if(v == pre) continue;
dfs1(v, u); siz[u] += siz[v];
if(siz[v] > maxn) son[u] = v, maxn = siz[v];
}
}
void dfs2(int u, int Top) {
dfn2[u] = ++tim2, tp[u] = Top;
if(son[u]) dfs2(son[u], Top);
for(int i = head2[u]; i; i = edge2[i].Next) {
int v = edge2[i].To;
if(v != son[u] && v != fa[u]) dfs2(v, v);
}
}
int Min_Past(int x, int y) {
int res = INF;
while(tp[x] != tp[y]) {
if(dep[tp[x]] < dep[tp[y]]) Swap(x, y);
res = Min(res, tree1.Query_Min(1, dfn2[tp[x]], dfn2[x]));
x = fa[tp[x]];
}
if(dep[x] > dep[y]) Swap(x, y);
res = Min(res, tree1.Query_Min(1, dfn2[x], dfn2[y]));
if(x > n) res = Min(res, w[fa[x]]);
return res;
}
int main() {
Read(n, m, q); cnt = n;
for(int i = 1; i <= n; i++) Read(w[i]);
for(int i = 1, u, v; i <= m; i++)
Read(u, v), Addedge1(u, v);
Tarjan(1);
dfs1(1, 0); dfs2(1, 1);
tree1.Build(1, 1, tim2);
tree2.Build();
for(int i = 2; i <= n; i++) tree2.Insert(tree2.root[fa[i] - n], w[i], 0);
for(int i = 1; i <= n; i++) tree1.Update(1, dfn2[i], w[i]);
for(int i = n + 1; i <= cnt; i++)
tree1.Update(1, dfn2[i], tree2.S(tree2.root[i - n]) == 2 ? INF : tree2.Query_Min(tree2.root[i - n]));
char opt[2];
for(int i = 1, a, b; i <= q; i++) {
scanf("%s", opt), Read(a, b);
if(opt[0] == 'A') printf("%d\n", Min_Past(a, b));
else {
if(a != 1) {
tree2.Remove(tree2.root[fa[a] - n], w[a]);
tree2.Insert(tree2.root[fa[a] - n], b, 0);
int tmp = tree2.Query_Min(tree2.root[fa[a] - n]);
tree1.Update(1, dfn2[fa[a]], tmp);
}
tree1.Update(1, dfn2[a], b), w[a] = b;
}
}
return 0;
}