E. Tourists

time limit per test:

2 seconds

memory limit per test:

256 megabytes

input:

standard input

output

:standard output

There are n cities in Cyberland, numbered from 1 to n, connected by m bidirectional roads. The j-th road connects city aj and bj.

For tourists, souvenirs are sold in every city of Cyberland. In particular, city i sell it at a price of wi.

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 [x1, x2, ..., xk], where k is a certain positive integer.
  • For any 1 ≤ i < j ≤ k, xi ≠ xj.
  • For any 1 ≤ i < k, there is a road connecting xi and xi + 1.
  • The minimum price of the route is min(wx1, wx2, ..., wxk).
  • The required answer is the minimum value of the minimum prices of all valid routes from a to b.

Input

The first line of input contains three integers n, m, q (1 ≤ n, m, q ≤ 105), separated by a single space.

Next n lines contain integers wi (1 ≤ wi ≤ 109).

Next m lines contain pairs of space-separated integers aj and bj (1 ≤ aj, bj ≤ n, aj ≠ 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 ≤ a, b ≤ n, 1 ≤ w ≤ 109).

Output

For each query of type "A", output the corresponding answer.

Examples

input
  1. 3 3 3
    1
    2
    3
    1 2
    2 3
    1 3
    A 2 3
    C 1 5
    A 2 3
output
  1. 1
    2
input
  1. 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

output

  1. 2
    1
    5
    3

Note

For the second sample, an optimal routes are:

From 2 to 3 it is [2, 3].

From 6 to 4 it is [6, 5, 1, 2, 4].

From 6 to 7 it is [6, 5, 7].

From 3 to 3 it is [3].

Solution

中文题面见UOJ#30  : UOJ#30

思路比较显然,把图缩点重建,再利用数据结构去维护。

要求简单路径,显然可以考虑点双连通分量,我们先对图Tarjan求点双连通分量,然后将这些BCC维护一个最小值,然后缩成一个点,这样就形成一棵树,就可以树剖。

但是这里有一个问题,割点可能会存在于多个点双连通分量中,而这会比较蛋疼,因为割点的答案显然应该是距离它最小的那个。

所以我们对割点单独处理,每个割点新建一个点单独向它所在的所有点双连通分量连一条边; 这样我们就可以在询问的时候,搞定割点的问题了。

那么修改一个点,这个点所在点双的值就有可能发生改变,如果修改割点,那么就会对很多点双造成影响,所以要特殊处理这种情况。

但是我们发现,我们这样建出来的新图(树)一定是满足 块->割点->块->割点 的。

所以我们不妨定义一个点双维护的是,这个点双中除了它fa的割点的所有点的权值+连向它的割点的权值最小,这样如果修改一个割点,就只会影响到它的fa点双。

查询的时候,我们查询$<u,v>$,如果$LCA(u,v)$是一个割点,我们可以直接统计答案。 如果$LCA(u,v)$是一个点双,那么我们还得查它的fa割点的值。

点双内的最小值,可以用multiset来维护。

这样的查询复杂度是$O(log^{2}N)$,修改的复杂度是$O(logN)$,总的复杂度就是$O(Nlog^{2}N)$

对于Tarjan点双连通分量,网上有些人说要边入栈,但很多人却写的点入栈,其实都是可以的。 但是这道题,如果用边入栈的方法,在连边的时候会连出问题的,需要额外判,所以就用来点入栈的方法。

Code

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cmath>
  5. #include<vector>
  6. #include<set>
  7. #include<cstring>
  8. using namespace std;
  9. inline int read()
  10. {
  11. int x=; char ch=getchar();
  12. while (ch<'' || ch>'') {ch=getchar();}
  13. while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
  14. return x;
  15. }
  16. #define INF 0x7fffffff
  17. #define MAXN 100100
  18. int N,M,Q,val[MAXN],Val[MAXN<<];
  19. struct RoadNode{int next,to;}road[MAXN<<];
  20. int tot=,first[MAXN];
  21. struct EdgeNode{int next,to;}edge[MAXN<<];
  22. int cnt=,head[MAXN<<];
  23. inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
  24. inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
  25. multiset<int>::iterator ist;
  26. struct BlockNode
  27. {
  28. multiset<int>st; int val;
  29. inline void insert(int x) {st.insert(x); val=*st.begin();}
  30. inline void change(int x,int y) {st.erase(*st.find(x)); st.insert(y); val=*st.begin();}
  31. }B[MAXN<<];
  32. namespace Graph
  33. {
  34. inline void AddRoad(int u,int v) {tot++; road[tot].next=first[u]; first[u]=tot; road[tot].to=v;}
  35. inline void InsertRoad(int u,int v) {AddRoad(u,v); AddRoad(v,u);}
  36. int dfn[MAXN],low[MAXN],dfsn,bcc,now,belong[MAXN],size[MAXN],cut[MAXN],st[MAXN],top;
  37. inline void Tarjan(int now)
  38. {
  39. dfn[now]=low[now]=++dfsn; st[++top]=now;
  40. for (int i=first[now]; i; i=road[i].next)
  41. if (!dfn[road[i].to])
  42. {
  43. Tarjan(road[i].to); low[now]=min(low[now],low[road[i].to]);
  44. if (dfn[now]<=low[road[i].to])
  45. {
  46. bcc++; cut[now]=; int tp=;
  47. while ()
  48. {
  49. tp=st[top--]; belong[tp]=bcc;
  50. if (cut[tp]) InsertEdge(tp+N,bcc); if (tp==road[i].to) break;
  51. }
  52. InsertEdge(now+N,bcc);
  53. }
  54. }
  55. else low[now]=min(low[now],dfn[road[i].to]);
  56. }
  57. inline void reBuild()
  58. {
  59. for (int i=; i<=N; i++) if (!dfn[i]) Tarjan(i);
  60. for (int i=; i<=N; i++)
  61. {
  62. if (i!=) B[belong[i]].insert(val[i]);
  63. if (cut[i]) B[i+N].insert(INF);
  64. }
  65. }
  66. }
  67. namespace SegmentTree
  68. {
  69. struct SegmentTreeNode{int l,r,minx;}tree[MAXN<<];
  70. #define ls now<<1
  71. #define rs now<<1|1
  72. inline void Update(int now) {tree[now].minx=min(tree[ls].minx,tree[rs].minx);}
  73. inline void BuildTree(int now,int l,int r)
  74. {
  75. tree[now].l=l; tree[now].r=r;
  76. if (l==r) {tree[now].minx=Val[l]; return;}
  77. int mid=(l+r)>>;
  78. BuildTree(ls,l,mid); BuildTree(rs,mid+,r);
  79. Update(now);
  80. }
  81. inline void Change(int now,int pos,int D)
  82. {
  83. int l=tree[now].l,r=tree[now].r;
  84. if (l==r) {tree[now].minx=D; return;}
  85. int mid=(l+r)>>;
  86. if (pos<=mid) Change(ls,pos,D);
  87. if (pos>mid) Change(rs,pos,D);
  88. Update(now);
  89. }
  90. inline int Query(int now,int L,int R)
  91. {
  92. int l=tree[now].l,r=tree[now].r;
  93. if (L<=l && R>=r) return tree[now].minx;
  94. int mid=(l+r)>>,re=INF;
  95. if (L<=mid) re=min(re,Query(ls,L,R));
  96. if (R>mid) re=min(re,Query(rs,L,R));
  97. return re;
  98. }
  99. }
  100. namespace TreePartition
  101. {
  102. int fa[MAXN<<],size[MAXN<<],son[MAXN<<],deep[MAXN<<],pl[MAXN<<],dfn,pre[MAXN<<],top[MAXN<<];
  103. inline void DFS_1(int now)
  104. {
  105. size[now]=;
  106. for (int i=head[now]; i; i=edge[i].next)
  107. if (edge[i].to!=fa[now])
  108. {
  109. fa[edge[i].to]=now;
  110. deep[edge[i].to]=deep[now]+;
  111. DFS_1(edge[i].to);
  112. size[now]+=size[edge[i].to];
  113. if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to;
  114. }
  115. }
  116. inline void DFS_2(int now,int chain)
  117. {
  118. pl[now]=++dfn; pre[dfn]=now; top[now]=chain; Val[dfn]=B[now].val;
  119. if (son[now]) DFS_2(son[now],chain);
  120. for (int i=head[now]; i; i=edge[i].next)
  121. if (edge[i].to!=fa[now] && edge[i].to!=son[now])
  122. DFS_2(edge[i].to,edge[i].to);
  123. }
  124. inline int LCA(int u,int v)
  125. {
  126. while (top[u]!=top[v])
  127. {
  128. if (deep[top[u]]<deep[top[v]]) swap(u,v);
  129. u=fa[top[u]];
  130. }
  131. if (deep[u]>deep[v]) swap(u,v);
  132. return u;
  133. }
  134. inline int Query(int x,int y)
  135. {
  136. x=Graph::cut[x]? x+N:Graph::belong[x]; y=Graph::cut[y]? y+N:Graph::belong[y];
  137. int re=INF; int lca=LCA(x,y);
  138. if (lca<=Graph::bcc) lca=fa[lca]; re=val[lca-N];
  139. while (top[x]!=top[y])
  140. {
  141. if (deep[top[x]]<deep[top[y]]) swap(x,y);
  142. re=min(re,SegmentTree::Query(,pl[top[x]],pl[x]));
  143. x=fa[top[x]];
  144. }
  145. if (deep[x]>deep[y]) swap(x,y);
  146. re=min(re,SegmentTree::Query(,pl[x],pl[y]));
  147. return re;
  148. }
  149. inline void Change(int pos,int D) {SegmentTree::Change(,pl[pos],D);}
  150. }
  151. int main()
  152. {
  153. N=read(),M=read(),Q=read();
  154. for (int i=; i<=N; i++) val[i]=read();
  155. for (int x,y,i=; i<=M; i++) x=read(),y=read(),Graph::InsertRoad(x,y);
  156. Graph::reBuild();
  157. // for (int i=1; i<=N; i++) printf("%d %d %d %d\n",dfn[i],low[i],cut[i],belong[i]);
  158. TreePartition::DFS_1(N+); TreePartition::DFS_2(N+,N+);
  159. SegmentTree::BuildTree(,,TreePartition::dfn);
  160. while (Q--)
  161. {
  162. char opt[]; scanf("%s",opt); int x=read(),y=read();
  163. switch (opt[])
  164. {
  165. case 'C': if (x!=) {B[Graph::belong[x]].change(val[x],y); TreePartition::Change(Graph::belong[x],B[Graph::belong[x]].val);} val[x]=y; break;
  166. case 'A': if (x==y) printf("%d\n",val[x]); else printf("%d\n",TreePartition::Query(x,y)); break;
  167. }
  168. }
  169. return ;
  170. }

UOJ上跑的还挺快QAQ...就是死活压不到6000ms+....

【Codefoces487E/UOJ#30】Tourists Tarjan 点双连通分量 + 树链剖分的更多相关文章

  1. CF487 E. Tourists [点双连通分量 树链剖分 割点]

    E. Tourists 题意: 无向连通图 C a w: 表示 a 城市的纪念品售价变成 w. A a b: 表示有一个游客要从 a 城市到 b 城市,你要回答在所有他的旅行路径中最低售价的最低可能值 ...

  2. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

  3. BZOJ 4732 UOJ #268 [清华集训2016]数据交互 (树链剖分、线段树)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4732 (UOJ) http://uoj.ac/problem/268 题解 ...

  4. 2018.10.30 NOIP训练 【模板】树链剖分(换根树剖)

    传送门 纯粹是为了熟悉板子. 然后发现自己手生了足足写了差不多25min而且输出的时候因为没开long longWA了三次还不知所云 代码

  5. uoj 30 tourists

    题目大意: 一个无向图 每个点有权值 支持两个操作 1 修改某个点的权值 2 查询a-b所有简单路径的点上的最小值 思路: 可以把图变成圆方树 然后树链剖分 维护 对于每个方点使用可删堆维护 #inc ...

  6. BZOJ_4326_[NOIP2015]_运输计划_(二分+LCA_树链剖分/Tarjan+差分)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=4326 给出一棵带有边权的树,以及一系列任务,任务是从树上的u点走到v点,代价为u到v路径上的权 ...

  7. JZYZOJ1454 NOIP2015 D2T3_运输计划 二分 差分数组 lca tarjan 树链剖分

    http://172.20.6.3/Problem_Show.asp?id=1454 从这道题我充分认识到我的脑子里好多水orz. 如果知道了这个要用二分和差分写,就没什么思考上的难点了(屁咧你写了一 ...

  8. CF487E Tourists(圆方树+树链剖分+multiset/可删堆)

    CF487E Tourists(圆方树+树链剖分+multiset/可删堆) Luogu 给出一个带点权的无向图,两种操作: 1.修改某点点权. 2.询问x到y之间简单路径能走过的点的最小点权. 题解 ...

  9. UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...

随机推荐

  1. 安装mysql后的基本配置

    1.添加环境变量 右键 此电脑->属性->高级系统设置->环境变量,在系统变量里面找到Path,双击.点击编辑,将mysql中bin文件的路径添加到最后一行,如:F:\AppSev\ ...

  2. 穿越之旅之--android中如何执行java命令

    android的程序基于java开发,当我们接上调试器,执行adb shell,就可以执行linux命令,但是却并不能执行java命令. 那么在android的shell中是否就不能执行java程序了 ...

  3. SVN 提交失败: permission denied - txn-current-lock

    执行以下命令即可 sudo chown -R www-data:subversion myproject sudo chmod -R g+rws myproject

  4. WPF系列 自定控件

    引言 WPF中微软提供了一些基本的控件,但是工作中这些基础的控件往往不能满足我们的需求,这个时候我们就需要根据实际的需求去开发自己的控件,但要注意不是所有功能不满足的情况都需要通过自定义控件来实现.实 ...

  5. Android APP 读取 AndroidManifest.xml 中的版本信息详解

    APP都会涉及到版本的问题,Android APP的版本信息保存在AndroidManifest.xml文件的顶部.如下图: 有2个属性表示,“android:versionCode”和“androi ...

  6. JS -- 异步加载进度条

    今天在博客园问答里面看到博友问道怎么实现Ajax异步加载产生进度条. 很好奇就自己写了一个. 展现效果: 1) 当点击Load的时候,模拟执行异步加载. 浏览器被遮挡. 进度条出现. 实现思路: 1. ...

  7. 【2016-11-11】【坚持学习】【Day24】【WPF 自定义控件 附加属性 自定义事件】

    UserControl ,自定义控件. 这里刚刚想到一个问题.什么时候应该用usercontrol 定义一个控件.什么时候应该重写控件的template和样式,实现新效果. 引用一下人家的话:http ...

  8. PHP实现多图片上传

    PHP实现多图片上传 今天在工作中遇到了一个需求:一个表单实现多个上传图片,类似于QQ空间上传照片的模式.即:可以一次性上传多个图片,但是封面图片只有一个. 最先,最重要的事,在服务器上对文件进行读写 ...

  9. iOS 2D绘图 (Quartz2D)之Transform(CTM,Translate,Rotate,scale)

    前言:Quartz默认采用设备无关的user space来进行绘图,当context(画板)建立之后,默认的坐标系原点以及方向也就确认了,可以通过CTM(current transformation ...

  10. PAT 1037. 在霍格沃茨找零钱(20)

    如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 -- 就如海格告诉哈利的:"十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易.& ...