<h2>题目描述</h2>
给定 $n$ 个点 $m$ 条边的连通点权无向图。
有 $q$ 个操作,要么修改一个点的点权,要么询问两点间经过一条从 $s$ 到 $t$ 的简单路径,路径上最小点权最小值是多少。
$ n,m,q \leq 10^5 $.
<h2>题解</h2>
问的是所有可能路径上的最小点权的最小值,那我们来考虑一下路径的性质。
首先我们可以发现,如果将这个图分成几个点双联通分量的话,路径一定是经过了从 $s$ 到 $t$ 的点双连通分量,而在每一个点双里是可以随便选择一个点经过的。所以这个问题就变成了:求经过的点双的点权最小值的最小值。
我们考虑建出圆方树,考虑方点维护该边双内圆点权值的最小值,每次修改的时候暴力修改该圆点所连接的方点记录的信息,然后就变成查询树上路径点权最小值了,可以使用树链剖分求解。
但是注意到如果我们构造一张菊花图,那么每次对于圆点的修改就退化为 $O(n)$ 了,不能接受。
不如我们改变方点维护的东西:我们维护除了父亲圆点的圆点点权最小值,查询的时候额外考虑方点父亲对答案的影响就好了。可以使用 multiset 维护。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define lc (x<<1)
#define rc (x<<1|1)
#define U unsigned
#define Re register
#define LL long long
#define FOR(i,a,b) for(Re int i = a;i <= b;++i)
#define ROF(i,a,b) for(Re int i = a;i >= b;--i)
#define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c)
#define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c)
#define CLR(i,a) memset(i,a,sizeof(i))
#define BR printf("--------------------n")
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 300000+5;
struct Edge{
int to,next;
}e1[MAXN<<1],e2[MAXN<<1];
int head1[MAXN],head2[MAXN],dfn[MAXN],low[MAXN],st[MAXN],top,cnt1,cnt2;
int dep[MAXN],size[MAXN],fa[MAXN],son[MAXN],tp[MAXN],idx[MAXN];
int bel[MAXN],tot,min[MAXN<<2],val[MAXN],N,M,Q;
std::multiset<int> S[MAXN];
inline void add1(int u,int v){
e1[++cnt1] = (Edge){v,head1[u]};head1[u] = cnt1;
}
inline void add2(int u,int v){
e2[++cnt2] = (Edge){v,head2[u]};head2[u] = cnt2;
}
void dfs1(int v){
static int ts = 0;
dfn[v] = low[v] = ++ts;st[++top] = v;
for(int i = head1[v];i;i = e1[i].next){
if(!dfn[e1[i].to]){
dfs1(e1[i].to);
low[v] = std::min(low[v],low[e1[i].to]);
if(low[e1[i].to] >= dfn[v]){
int r = -1;tot++;
do{
r = st[top--];
add2(tot,r);add2(r,tot);
}while(r != e1[i].to);
add2(v,tot);add2(tot,v);
}
}
else low[v] = std::min(low[v],dfn[e1[i].to]);
}
}
void dfs2(int v,int faa){
size[v] = 1;int max = 0;
for(int i = head2[v];i;i = e2[i].next){
if(e2[i].to != faa){
dep[e2[i].to] = dep[v] + 1;
dfs2(e2[i].to,v);size[v] += size[e2[i].to];
fa[e2[i].to] = v;
if(size[e2[i].to] > max){
max = size[e2[i].to];
son[v] = e2[i].to;
}
}
}
}
void dfs3(int v,int t){
static int ts = 0;
dfn[v] = ++ts;tp[v] = t;idx[dfn[v]] = v;
if(son[v]) dfs3(son[v],t);
for(int i = head2[v];i;i = e2[i].next){
if(e2[i].to != fa[v] && e2[i].to != son[v]){
dfs3(e2[i].to,e2[i].to);
}
}
}
inline void pushup(int x){
min[x] = std::min(min[lc],min[rc]);
}
inline void build(int x,int l,int r){
//DEBUG(l);DEBUG(r);
if(l == r){
min[x] = val[idx[l]];
return;
}
int mid = (l + r) >> 1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(x);
}
void update(int x,int l,int r,int pos,int d){
if(l == r){
min[x] = d;return;
}
int mid = (l + r) >> 1;
if(pos <= mid) update(lc,l,mid,pos,d);
else update(rc,mid+1,r,pos,d);
pushup(x);
}
int query(int x,int l,int r,int L,int R){
if(l == L && r == R) return min[x];
int mid = (l + r) >> 1;
if(R <= mid) return query(lc,l,mid,L,R);
if(L > mid) return query(rc,mid+1,r,L,R);
return std::min(query(lc,l,mid,L,mid),query(rc,mid+1,r,mid+1,R));
}
int calc(int u,int v){
int res = INT_MAX;
while(tp[u] != tp[v]){
if(dep[tp[u]] < dep[tp[v]]) std::swap(u,v);
res = std::min(res,query(1,1,tot,dfn[tp[u]],dfn[u]));
u = fa[tp[u]];
}
if(dep[u] > dep[v]) std::swap(u,v);
res = std::min(res,query(1,1,tot,dfn[u],dfn[v]));
if(u > N) res = std::min(res,val[fa[u]]);
return res;
}
int main(){
scanf("%d%d%d",&N,&M,&Q);tot = N;
FOR(i,1,N) scanf("%d",val+i);
FOR(i,1,M){
int u,v;scanf("%d%d",&u,&v);add1(u,v);add1(v,u);
}
FOR(i,1,N) if(!dfn[i]) dfs1(i);
CLR(dfn,0);dfs2(1,0);dfs3(1,1);
FOR(i,2,N) S[fa[i]-N].insert(val[i]);
FOR(i,N+1,tot) val[i] = S[i-N].empty() ? INT_MAX : *S[i-N].begin();
build(1,1,tot);
while(Q--){
char opt;scanf("%c",&opt);
while(opt != 'C' && opt != 'A') scanf("%c",&opt);
int u,v;scanf("%d%d",&u,&v);
if(opt == 'C'){
update(1,1,tot,dfn[u],v);
if(u == 1) {val[u] = v;continue;}
int o = fa[u];
S[o-N].erase(S[o-N].find(val[u]));
S[o-N].insert(v);
int minv = *S[o-N].begin();
if(minv == val[o]) {val[u] = v;continue;}
update(1,1,tot,dfn[o],minv);
val[o] = minv;val[u] = v;
}
else{
printf("%dn",calc(u,v));
}
}
return 0;
}