题目链接:https://www.luogu.com.cn/problem/CF487E
Tourists
题面翻译
Cyberland 有 $n$ 座城市,编号从 $1$ 到 $n$,有 $m$ 条双向道路连接这些城市。第 $j$ 条路连接城市 $a_j$ 和 $b_j$。每天,都有成千上万的游客来到 Cyberland 游玩。
在每一个城市,都有纪念品售卖,第 $i$ 个城市售价为 $w_i$。这个售价有时会变动。
每一个游客的游览路径都有固定起始城市和终止城市,且不会经过重复的城市。
他们会在路径上的城市中,售价最低的那个城市购买纪念品。
你能求出每一个游客在所有合法的路径中能购买的最低售价是多少吗?
你要处理 $q$ 个操作:
C a w
: 表示 $a$ 城市的纪念品售价变成 $w$。
A a b
: 表示有一个游客要从 $a$ 城市到 $b$ 城市,你要回答在所有他的旅行路径中最低售价的最低可能值。
输入格式
第一行包含用一个空格隔开的三个数 $n, m, q$。
接下来 $n$ 行,每行包含一个数 $w_i$。
接下来 $m$ 行,每行包含用一个空格隔开的两个数 $a_j$,$b_j$。($1 \le a _ j, b _ j \le n,a _ j \neq b _ j$)
数据保证没有两条道路连接同样一对城市,也没有一条道路两端是相同的城市。并且任意两个城市都可以相互到达。
接下来 $q$ 行,每行是 C a w
或 A a b
,描述了一个操作。
输出格式
对于每一个 A 类操作,输出一行表示对应的答案。
题目描述
There are $ n $ cities in Cyberland, numbered from $ 1 $ to $ n $ , connected by $ m $ bidirectional roads. The $ j $ -th road connects city $ a_{j} $ and $ b_{j} $ .
For tourists, souvenirs are sold in every city of Cyberland. In particular, city $ i $ sell it at a price of $ w_{i} $ .
Now there are $ q $ queries for you to handle. There are two types of queries:
- "C $ a $ $ w $ ": The price in city $ a $ is changed to $ w $ .
- "A $ a $ $ b $ ": Now a tourist will travel from city $ a $ to $ b $ . He will choose a route, he also doesn't want to visit a city twice. He will buy souvenirs at the city where the souvenirs are the cheapest (possibly exactly at city $ a $ or $ b $ ). You should output the minimum possible price that he can buy the souvenirs during his travel.
More formally, we can define routes as follow:
- A route is a sequence of cities $ [x_{1},x_{2},...,x_{k}] $ , where $ k $ is a certain positive integer.
- For any $ 1<=i<j<=k,x_{i}≠x_{j} $ .
- For any $ 1<=i<k $ , there is a road connecting $ x_{i} $ and $ x_{i+1} $ .
- The minimum price of the route is $ min(w_{x1},w_{x2},...,w_{xk}) $ .
- The required answer is the minimum value of the minimum prices of all valid routes from $ a $ to $ b $ .
输入格式
The first line of input contains three integers $n, m, q (1 \leq n, m, q \leq 10^5)$, separated by a single space.
Next n lines contain integers $w_i (1 \leq w_i \leq 10^9)$.
Next m lines contain pairs of space-separated integers $a_j$ and $b_j$ $(1 \leq a_j, b_j \leq n, aj \neq bj)$.
It is guaranteed that there is at most one road connecting the same pair of cities. There is always at least one valid route between any two cities.
Next $q$ lines each describe a query. The format is "C a w" or "A a b" $(1 \leq a, b \leq n, 1 \leq w \leq 10^9)$.
输出格式
For each query of type "A", output the corresponding answer.
样例 #1
样例输入 #1
3 3 3
1
2
3
1 2
2 3
1 3
A 2 3
C 1 5
A 2 3
样例输出 #1
1
2
样例 #2
样例输入 #2
7 9 4
1
2
3
4
5
6
7
1 2
2 5
1 5
2 3
3 4
2 4
5 6
6 7
5 7
A 2 3
A 6 4
A 6 7
A 3 3
样例输出 #2
2
1
5
3
提示
There are $ n $ cities in Cyberland, numbered from $ 1 $ to $ n $ , connected by $ m $ bidirectional roads. The $ j $ -th road connects city $ a_{j} $ and $ b_{j} $ .
For tourists, souvenirs are sold in every city of Cyberland. In particular, city $ i $ sell it at a price of $ w_{i} $ .
Now there are $ q $ queries for you to handle. There are two types of queries:
- "C $ a $ $ w $ ": The price in city $ a $ is changed to $ w $ .
- "A $ a $ $ b $ ": Now a tourist will travel from city $ a $ to $ b $ . He will choose a route, he also doesn't want to visit a city twice. He will buy souvenirs at the city where the souvenirs are the cheapest (possibly exactly at city $ a $ or $ b $ ). You should output the minimum possible price that he can buy the souvenirs during his travel.
More formally, we can define routes as follow:
- A route is a sequence of cities $ [x_{1},x_{2},...,x_{k}] $ , where $ k $ is a certain positive integer.
- For any $ 1<=i<j<=k,x_{i}≠x_{j} $ .
- For any $ 1<=i<k $ , there is a road connecting $ x_{i} $ and $ x_{i+1} $ .
- The minimum price of the route is $ min(w_{x1},w_{x2},...,w_{xk}) $ .
- The required answer is the minimum value of the minimum prices of all valid routes from $ a $ to $ b $ .
思路:题目给的是一张图,而我们需要维护的是两点之间的路径的最小值,我们比较熟悉的是在一棵树上树链剖分维护路径的最值,可以想到将这张图转换成一棵树,如果这张图是有向图的话,我们可以用tarjan求强连通分量然后缩点,将环缩成点,那么图就转换成一棵树了,但题目给的是一张无向图,此时我们就需要用到一个叫圆方树的东西,不会的可以自学一下,这里就不再赘述了。我们建出圆方树之后,就可以直接对它树链剖分维护路径最小值,但是我们怎么给点赋值呢,一个办法是可以将圆方树中的圆点的值设为他本身的值,将圆方树中的方点设为他连接的所有圆点的最小值,但是这种情况下,每次修改点的值时,需要同时把包括这个点的所有方点都修改,如果这个点连接了很多个方点的话,那么每次询问复杂度可能会到nlogn级别,显然这样是会超时的,解决办法是,我们设方点的值时,将方点的值设为他所有儿子的最小值,那么我们修改某个点的值时,我们只需要修改这个点的父亲节点(修改根节点时可以不用考虑),但是找路径最小值时,到最后要和找到的lca和他的父亲比较取最小值,那么为什么可以这么做呢,我们知道方点的值是和他周围圆点的值相关的,圆点修改要修改他连接的所有方点,但是为什么可以不考虑修改他的父亲节点(也是一个方点),我们树链剖分最后会找到一个lca,这个lca肯定是一个方点,我们不需要考虑修改父亲节点就是因为我们最后要和lca的父亲比较取最小值。我们可以用multiset存每个方点的儿子值,修改点的值时,只需要修改他的父亲方点的值,找到父亲方点中的这个节点的信息,然后存入新的值,更新这个方点的最小值,大概就是这么些,具体可以看代码。
代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<vector>
4 #include<map>
5 #include<queue>
6 #include<set>
7 #include<algorithm>
8 #include<stack>
9 #include<cmath>
10 #include<cstring>
11 #include<string>
12 using namespace std;
13 #define gc getchar()
14 #define rd(x) read(x)
15 #define el '\n'
16 #define rep(i, a, n) for(int i = (a); i <= n; ++i)
17 #define per(i, a, n) for(int i = (a); i >= n; --i)
18 using ll = long long;
19 using db = double;
20 using ldb = long double;
21 const int N = 500000 + 10;
22 const int mod = 1e9 + 7;
23 const int inf = 0x3f3f3f3f;
24
25 template <typename _T>
26 inline void read(_T& f) {
27 f = 0; _T fu = 1; char c = gc;
28 while (c < '0' || c > '9') { if (c == '-') { fu = -1; } c = gc; }
29 while (c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = gc; }
30 f *= fu;
31 }
32
33 template <typename T>
34 void print(T x) {
35 if (x < 0) putchar('-'), x = -x;
36 if (x < 10) putchar(x + 48);
37 else print(x / 10), putchar(x % 10 + 48);
38 }
39
40 template <typename T>
41 void print(T x, char t) {
42 print(x); putchar(t);
43 }
44
45 int w[N], rnk[N];
46
47 struct SegTree {
48 int sum[N << 4];
49 void PushUp(int rt) {
50 sum[rt] = min(sum[rt << 1], sum[rt << 1 | 1]);
51 }
52 void Build(int l, int r, int rt) {
53 if (l == r) {
54 sum[rt] = w[rnk[l]];
55 return;
56 }
57 int m = (l + r) >> 1;
58 Build(l, m, rt << 1);
59 Build(m + 1, r, rt << 1 | 1);
60 PushUp(rt);
61 }
62 void Update(int L, int C, int l, int r, int rt) {
63 if (l == r) {
64 sum[rt] = C;
65 return;
66 }
67 int m = (l + r) >> 1;
68 if (m >= L) Update(L, C, l, m, rt << 1);
69 else Update(L, C, m + 1, r, rt << 1 | 1);
70 PushUp(rt);
71 }
72 int Query(int L, int R, int l, int r, int rt) {
73 if (l >= L && r <= R) {
74 return sum[rt];
75 }
76 int m = (l + r) >> 1;
77 int ans1 = inf, ans2 = inf;
78 if (m >= L) ans1 = Query(L, R, l, m, rt << 1);
79 if (m < R) ans2 = Query(L, R, m + 1, r, rt << 1 | 1);
80 return min(ans1, ans2);
81 }
82 }st;
83
84 struct node {
85 int to, w, next;
86 }e[N << 2];
87 int head[N << 2], tot;
88
89 void add(int u, int v) {
90 e[tot].to = v;
91 //e[tot].w = w;
92 e[tot].next = head[u];
93 head[u] = tot++;
94 }
95
96 int dep[N], fa[N], siz[N], son[N];
97
98 void dfs1(int x) {
99 siz[x] = 1;
100 for (int i = head[x]; i + 1; i = e[i].next) {
101 int to = e[i].to;
102 if (to == fa[x]) continue;
103 dep[to] = dep[x] + 1;
104 fa[to] = x;
105 dfs1(to);
106 siz[x] += siz[to];
107 if (siz[to] > siz[son[x]]) son[x] = to;
108 }
109 }
110
111 int top[N], ctr[N], dfn[N], cnt;
112
113 void dfs2(int x, int tp) {
114 //cout << x << ' ';
115 top[x] = tp;
116 dfn[x] = ++cnt;
117 rnk[cnt] = x;
118 if (son[x]) dfs2(son[x], tp);
119 for (int i = head[x]; i + 1; i = e[i].next) {
120 int to = e[i].to;
121 if (to != son[x] && to != fa[x]) dfs2(to, to);
122 }
123 ctr[x] = cnt;
124 }
125
126 int n, m, q;
127
128 void getsum_xtoy() {
129 int x, y;
130 cin >> x >> y;
131 if (x == y) {
132 cout << w[x] << el;
133 return;
134 }
135 int ans = inf;
136 while (top[x] != top[y]) {
137 if (dep[top[x]] > dep[top[y]]) swap(x, y);
138 ans = min(st.Query(dfn[top[y]], dfn[y], 1, cnt, 1), ans);
139 y = fa[top[y]];
140 }
141 if (dep[x] > dep[y]) swap(x, y);
142 ans = min(st.Query(dfn[x], dfn[y], 1, cnt, 1), ans);
143 if (x > n) ans = min(w[fa[x]], ans);
144 cout << ans << "\n";
145 }
146
147 int low[N], dfc;
148 int stk[N], tp;
149 int bcc;
150
151 vector<int>G[N];
152 multiset<int>mst[N];
153
154 void tarjan(int u) {
155
156 dfn[u] = low[u] = ++dfc;
157 stk[++tp] = u;
158 for (int i = 0; i < (int)G[u].size(); i++) {
159 int v = G[u][i];
160 if (!dfn[v]) {
161 tarjan(v);
162 low[u] = min(low[u], low[v]);
163 if (low[v] == dfn[u]) {
164 bcc++;
165 //int minx = inf;
166 for (int x = 0; x != v; tp--) {
167 x = stk[tp];
168 add(x, bcc), add(bcc, x);
169 //minx = min(minx, w[x]);
170 }
171 add(u, bcc), add(bcc, u);
172 //w[bcc] = min(minx, w[u]);
173 }
174 }
175 else low[u] = min(low[u], dfn[v]);
176 }
177
178 }
179
180 int main() {
181
182 ios::sync_with_stdio(false);
183 cin.tie(0), cout.tie(0);
184 memset(head, -1, sizeof(head));
185 cin >> n >> m >> q;
186 bcc = n;
187 for (int i = 1; i <= n; i++) {
188 cin >> w[i];
189 }
190 for (int i = 1; i <= m; i++) {
191 int u, v;
192 cin >> u >> v;
193 G[u].push_back(v);
194 G[v].push_back(u);
195 }
196 tarjan(1);
197 // for(int i = n + 1; i <= bcc; i++) {
198 // for(int j = head[i]; j + 1; j = e[j].next) {
199 // cout << i << ' ' << e[j].to << el;
200 // }
201 // //cout << el;
202 // }
203 fa[1] = 0;
204 memset(dfn, 0, sizeof(dfn));
205 dfs1(1);
206 dfs2(1, 1);
207 //cout << el;
208 for (int i = n + 1; i <= bcc; i++) {
209 for (int j = head[i]; j + 1; j = e[j].next) {
210 int to = e[j].to;
211 if (to != fa[i]) mst[i].insert(w[to]);
212 }
213 w[i] = *mst[i].begin();
214 }
215 st.Build(1, cnt, 1);
216 for (; q; --q) {
217 char op;
218 cin >> op;
219 if (op == 'C') {
220 int x, p;
221 cin >> x >> p;
222 if (fa[x]) {
223 mst[fa[x]].erase(mst[fa[x]].find(w[x]));
224 mst[fa[x]].insert(p);
225 w[fa[x]] = *mst[fa[x]].begin();
226 st.Update(dfn[fa[x]], w[fa[x]], 1, cnt, 1);
227 }
228 w[x] = p;
229 st.Update(dfn[x], w[x], 1, cnt, 1);
230 }
231 else getsum_xtoy();
232 }
233
234 return 0;
235 }