CF487E Tourists(圆方树+树链剖分)

题目链接: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 wA 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 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值