cf487E tourists 树剖+线段树维护圆方树

(8 mins to read)

给定无向图,每个点有点权,多次询问两点间所有简单路径上的点权的最小值,并要求支持修改点权

考虑建出圆方树,将方点的点权置为所有相邻圆点的点权的最小值,那答案就是两个点在圆方树上的路径上的点权最小值。但是如果一个圆点和多个方点相邻,在修改的时候复杂度就炸了。此时的trick是让1为根,方点维护所有儿子的圆点的权值最小值,这样在修改时圆点只需要单点修改其父亲即可,而在查询时,如果lca是一个方点,再统计一下方点的父亲这个圆点的答案即可。树剖,线段树支持单点修改,区间查询最小值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, inf = 0x3f3f3f3f;
int n, m, q;
int low[N], dfn[N], stk[N], clk, top, cnt;
int val[N];
vector<int> G[N], T[N<<1];
void tarjan(int u, int fa)
{
low[u] = dfn[u] = ++clk;
stk[++top] = u;
for(int v : G[u])
{
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v]>=dfn[u])
{
++cnt;
for(int x=0; x!=v; top--)
{
x = stk[top];
T[cnt].push_back(x);
T[x].push_back(cnt);
}
T[cnt].push_back(u);
T[u].push_back(cnt);
}
}
else if(v!=fa) low[u] = min(low[u], dfn[v]);
}
}
const int M = N<<1;
multiset<int> mst[M];
namespace HLD
{
int sz[M], top[M], son[M], dep[M], fa[M], dfn[M], idfn[M], tot;
void dfs1(int u, int f)
{
dep[u] = dep[f] + 1; fa[u] = f;
sz[u] = 1;
if(u<=n) mst[u].insert(val[u]);
for(int v : T[u])
{
if(v!=f)
{
if(u>n) mst[u].insert(val[v]);
dfs1(v, u);
sz[u] += sz[v];
if(sz[v]>sz[son[u]]) son[u] = v;
}
}
}
void dfs2(int u, int t)
{
top[u] = t;
dfn[u] = ++tot;
idfn[tot] = u;
if(!son[u]) return;
dfs2(son[u], t);
for(int v : T[u])
if(v!=son[u]&&v!=fa[u]) dfs2(v, v);
}
}
using HLD::fa;
using HLD::idfn;
using HLD::dep;
struct seg
{
int l, r, mn;
}t[M<<2];
void pull(int p) { t[p].mn = min(t[p<<1].mn, t[p<<1|1].mn); }
void build(int p, int l, int r)
{
t[p].l = l, t[p].r = r;
if(l==r)
{
t[p].mn = *mst[idfn[l]].begin();
return;
}
int mid = (l+r)>>1;
build(p<<1, l, mid); build(p<<1|1, mid+1, r);
pull(p);
}
void upd(int p, int x, int y)
{
int l = t[p].l, r = t[p].r;
if(l==r)
{
t[p].mn = y;
return;
}
int mid = (l+r)>>1;
if(x<=mid) upd(p<<1, x, y);
else upd(p<<1|1, x, y);
pull(p);
}
int ask(int p, int x, int y)
{
int l = t[p].l, r = t[p].r;
if(l>=x && r<=y) return t[p].mn;
int mid = (l+r)>>1, ans = inf;
if(x<=mid) ans = min(ans, ask(p<<1, x, y));
if(y>mid) ans = min(ans, ask(p<<1|1, x, y));
return ans;
}
int askt(int x, int y)
{
int ans = inf;
while(HLD::top[x]!=HLD::top[y])
{
if(dep[HLD::top[x]]<dep[HLD::top[y]]) swap(x,y);
ans = min(ans, ask(1, HLD::dfn[HLD::top[x]], HLD::dfn[x]));
x = fa[HLD::top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans = min(ans, ask(1, HLD::dfn[x], HLD::dfn[y]));
if(x>n) ans = min(ans, val[fa[x]]);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> q;
for(int i=1; i<=n; i++) cin >> val[i];
for(int i=1; i<=m; i++)
{
int u, v; cin >> u >> v;
G[u].push_back(v); G[v].push_back(u);
}
cnt = n; tarjan(1, 0);
HLD::dfs1(1, 0), HLD::dfs2(1, 1);
build(1, 1, 2*n);
while(q--)
{
char op; int x, y;
cin >> op >> x >> y;
if(op=='C')
{
if(fa[x])
{
int pre = *mst[fa[x]].begin();
mst[fa[x]].erase(mst[fa[x]].find(val[x]));
mst[fa[x]].insert(y);
int cur = *mst[fa[x]].begin();
if(pre!=cur) upd(1, HLD::dfn[fa[x]], cur);
}
val[x] = y;
upd(1, HLD::dfn[x], y);
}
else cout << askt(x, y) << '\n';
}
return 0;
}