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.
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).
For each query of type "A", output the corresponding answer.
3 3 3 1 2 3 1 2 2 3 1 3 A 2 3 C 1 5 A 2 3
1 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 1 5 3
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].
题目大意:给出一个无向简单图,保证任意顶点之间是连通的。每个点有一个点权。
操作分为两种,一种是更改点的点权;另一种是给出起点和终点,求从起点到终点的路径中可能经过的最小点权。
题解:树链剖分+tarjan
给出点双连通分量的定义:对于一个连通图,如果任意两点至少存在两条点不重复路径,则称这个图为点双连通的(简称双连通)。那么同属一个点双连通分量的点,到达其中的一个点就一定能到达连通分量中的所有点。但是我们无法直接进行缩点,因为有些割点会属于很多的点双连通图分量。所以我们对于每个点双连通分量新建一个节点,将所有属于他的点都与他连边,然后我们会得到一颗树。这颗树有不错的性质,首先一定是原图的点与新建节点交替分布的(即不会存在两个原图的点直接相连)。所有非叶子节点的原图的点对应的都是原图的割点。这样我们就得到了一棵树,是所有的点都与他所属的点双相连,并且没有冲突。
那么我们就可以在这棵树上搞事情了。
对于点双的权值,我们定义为他所有儿子节点中的最小点权,这个需要用multiset来维护。
修改一个点的点权也需要对应的修改,他所属的点双(父节点)。
对于查询,因为我们只要经过点双中的一个点就可以获取点双中的最小权值。所以我们对树进行树链剖分,然后查询路径上的最小值即可。需要注意的是如果两个点的lca是新建的点双节点那么我们还要加入fa[lca]的权值,fa[lca]也是属于lca这点双的,只不过是我们没有维护进去。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#define N 500003
#define inf 1000000000
using namespace std;
int n,m,q,tot,cnt,sz,top,root,n1;
int belong[N],st[N],low[N],dfsn[N],bl[N],head[N],nxt[N],next[N],point[N],val[N];
int u[N],v[N],ins[N],vis[N],mark[N],tr[N*4],pos[N],p[N],fa[N],deep[N],size[N],son[N];
int bk[N];
multiset<int> c[N];
void add(int x,int y)
{
tot++; next[tot]=head[x]; head[x]=tot; u[tot]=y;
tot++; next[tot]=head[y]; head[y]=tot; u[tot]=x;
}
void addedge(int x,int y)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void tarjan(int x)
{
st[++top]=x; low[x]=dfsn[x]=++sz; ins[x]=1;
for (int i=head[x];i;i=next[i]) {
int j=u[i];
if (!dfsn[j]) {
tarjan(j);
low[x]=min(low[x],low[j]);
if (dfsn[x]<=low[j]) {
++cnt; mark[x]=1; int k;
while (1){
k=st[top--];
bl[k]=cnt;
addedge(cnt+n,k);
if (k==j) break;
}
addedge(cnt+n,x);
root=x;
}
}
else if (ins[j]) low[x]=min(low[x],dfsn[j]);
}
}
void search(int x)
{
addedge(cnt+n,x); vis[x]=cnt; bl[x]=cnt;
for (int i=head[x];i;i=next[i]) {
if (mark[u[i]]&&vis[u[i]]!=cnt) {
vis[u[i]]=cnt; addedge(cnt+n,u[i]);
continue;
}
if (vis[u[i]]) continue;
search(u[i]);
}
}
void dfs(int x,int f)
{
fa[x]=f; size[x]=1; deep[x]=deep[f]+1;
for (int i=point[x];i;i=nxt[i]) {
if (v[i]==f) continue;
dfs(v[i],x);
size[x]+=size[v[i]];
if (size[son[x]]<size[v[i]]) son[x]=v[i];
}
}
void dfs1(int x,int chain)
{
belong[x]=chain; pos[x]=++sz; p[sz]=x;
if (!son[x]) return;
dfs1(son[x],chain);
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa[x]&&v[i]!=son[x])
dfs1(v[i],v[i]);
}
void update(int now)
{
tr[now]=min(tr[now<<1],tr[now<<1|1]);
}
void build(int now,int l,int r)
{
if (l==r) {
tr[now]=val[p[l]];
return;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
void pointchange(int now,int l,int r,int x,int v)
{
if (l==r) {
tr[now]=v;
return;
}
int mid=(l+r)/2;
if (x<=mid) pointchange(now<<1,l,mid,x,v);
else pointchange(now<<1|1,mid+1,r,x,v);
update(now);
}
int find(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) return tr[now];
int mid=(l+r)/2; int ans=inf;
if (ll<=mid) ans=min(ans,find(now<<1,l,mid,ll,rr));
if (rr>mid) ans=min(ans,find(now<<1|1,mid+1,r,ll,rr));
return ans;
}
int solve(int x,int y)
{
int ans=inf;
while (belong[x]!=belong[y]) {
if (deep[belong[x]]<deep[belong[y]]) swap(x,y);
ans=min(ans,find(1,1,n,pos[belong[x]],pos[x]));
x=fa[belong[x]];
}
if (deep[x]>deep[y]) swap(x,y);
ans=min(ans,find(1,1,n,pos[x],pos[y]));
if (x>n1&&fa[x]) ans=min(ans,find(1,1,n,pos[fa[x]],pos[fa[x]]));
return ans;
}
int main()
{
freopen("tourists.in","r",stdin);
freopen("tourists.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=n;i++) scanf("%d",&val[i]);
for (int i=1;i<=m;i++) {
int x,y; scanf("%d%d",&x,&y);
add(x,y);
}
for (int i=1;i<=2*n;i++) bk[i]=i;
tot=0;
for (int i=1;i<=n;i++)
if (!dfsn[i]) tarjan(i);
if (cnt==1) root=cnt+n;
dfs(root,0);
sz=0; dfs1(root,root);
for (int i=n+1;i<=n+cnt;i++) val[i]=inf;
for (int i=1;i<=n;i++) {
if (i==root) continue;
bl[i]=fa[i]; int t=bl[i];
c[t].insert(val[i]);
val[t]=min(val[t],*c[t].begin());
}
n1=n; n+=cnt;
build(1,1,n);
for (int i=1;i<=q;i++) {
char opt[10]; int x,y; scanf("%s%d%d",opt,&x,&y);
if (opt[0]=='A') {
printf("%d\n",solve(x,y));
}
else {
pointchange(1,1,n,pos[x],y);
if (x==root) {
val[x]=y;
continue;
}
int t=bl[x];
c[t].erase(c[t].find(val[x])); val[x]=y;
c[t].insert(val[x]);
val[t]=*c[t].begin();
pointchange(1,1,n,pos[t],val[t]);
}
}
}