CF487E Tourists
题意
给定一张无向图,点有点权。每次可以修改一个点的点权,或者询问从 \(a\) 到 \(b\) 所有不经过重复点的路径上最小的点权是多少。
Solution
考虑一个点双,点双中任意两个点之间都有两条以上的不同路径,换句话说,对于一个点双,我从任意点出发到任何异于起点的点去,总能取到点双中点权最小的点。这提示我们,维护点双是没有意义的。
考虑如何把点双啊掉。不难想到圆方树。建完之后,在方点上记录这个点双中的最小点权,然后每次询问就相当于是询问路径最小值,直接树剖维护就行了。
但是这样直接做似乎能卡到 \(O(n^2)\),因为一个圆点连接的方点有很多,不能一个个改过去。
那怎么办捏。。。\(\bigstar\) 人类智慧!先定根,然后每个方点维护的是它所有儿子的最小值。这样,修改一个圆点的时候,就只需要修改它父亲的最小值就可以了。至于维护最小值,对每个点开一个 multiset
就可以了。
此时需要注意,如果在树剖的时候,LCA 是一个方点的话,其父亲的贡献也要加入。
Code
// Problem:
// Tourists
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF487E
// Memory Limit: 250 MB
// Time Limit: 2000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
const int MAXN=2e5+10;
vector<int> g[MAXN],e[MAXN];
multiset<int> mn[MAXN];
void add(int u,int v){e[u].pb(v);e[v].pb(u);}
int rk[MAXN],top[MAXN];
int w[MAXN],n,nod;
struct Tree{int l,r,mn;}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
void pushup(int i){tr[i].mn=min(tr[ls].mn,tr[rs].mn);}
void build(int i,int l,int r){
tr[i].l=l;tr[i].r=r;if(l==r){tr[i].mn=w[rk[l]];return;}
int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(i);
}
void upd(int i,int x,int v){
if(tr[i].l==tr[i].r){tr[i].mn=v;return;}int mid=(tr[i].l+tr[i].r)>>1;
if(x<=mid) upd(ls,x,v);else upd(rs,x,v);pushup(i);
}
int ask(int i,int l,int r){
if(tr[i].l==l&&tr[i].r==r) return tr[i].mn;int mid=(tr[i].l+tr[i].r)>>1;
if(r<=mid) return ask(ls,l,r);else if(l>mid) return ask(rs,l,r);
else return min(ask(ls,l,mid),ask(rs,mid+1,r));
}
int dfn[MAXN],low[MAXN],stk[MAXN],tp,tot;
void Tarjan(int x){
dfn[x]=low[x]=++tot;stk[++tp]=x;
for(int s:g[x]){
if(!dfn[s]){
Tarjan(s);
low[x]=min(low[x],low[s]);
if(low[s]>=dfn[x]){
nod++;add(nod,x);
do{add(nod,stk[tp]);
}while(stk[tp--]!=s);
}
}else low[x]=min(low[x],dfn[s]);
}
}
int son[MAXN],siz[MAXN],fa[MAXN],dep[MAXN];
void dfs1(int x,int f){
siz[x]=1;son[x]=-1;
for(int s:e[x]){
if(s==f) continue;
if(x>n) mn[x].insert(w[s]);
dep[s]=dep[x]+1;fa[s]=x;
dfs1(s,x);siz[x]+=siz[s];
if(son[x]==-1||siz[son[x]]<siz[s])
son[x]=s;
}if(x>n) w[x]=*mn[x].begin();
}
void dfs2(int x,int t){
dfn[x]=++tot;rk[tot]=x;
top[x]=t;
if(~son[x]) dfs2(son[x],t);
for(int s:e[x])
if(s!=son[x]&&s!=fa[x])
dfs2(s,s);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int m,Q;
cin>>n>>m>>Q;
rep(i,1,n) cin>>w[i];
rep(i,1,m){
int u,v;cin>>u>>v;
g[u].pb(v);g[v].pb(u);
}nod=n;
Tarjan(1);
memset(dfn,0,sizeof(dfn));tot=0;
dfs1(1,0);dfs2(1,1);
build(1,1,nod);
char op;int a,b;
while(Q--){
cin>>op>>a>>b;
if(op=='C'){
upd(1,dfn[a],b);
int f=fa[a];
if(f){
mn[f].erase(mn[f].find(w[a]));
mn[f].insert(b);w[f]=*mn[f].begin();
upd(1,dfn[f],w[f]);
}w[a]=b;
}else{
int ans=inf;
while(top[a]^top[b]){
if(dep[top[a]]<dep[top[b]]) swap(a,b);
ans=min(ans,ask(1,dfn[top[a]],dfn[a]));
a=fa[top[a]];
}if(dfn[a]>dfn[b]) swap(a,b);
ans=min(ans,ask(1,dfn[a],dfn[b]));
if(a>n) ans=min(ans,w[fa[a]]);
cout<<ans<<'\n';
}
}
return 0;
}