Tourists
$\quad $ 圆方树练手好题。
$\quad $ 大概意思就是给你一个仙人掌,其中每个点都有点权。有 \(m\) 次询问,其中有两种操作:回答两点间所有可能路径(不重复经过一个点)上的点权最小值、将某个点的点权改为某值。
$\quad $ 对于路径上点权最小值,可以先转化为圆方树,然后树链剖分解决,用方点维护与其相连点的点权最小值。
$\quad $ 对于更改,树剖更改即可。但是需要注意到,更改圆点时,也要同时更新与其相连的方点的权值,如果是菊花图的话,就有 \(TLE\) 的风险。
$\quad $ 所以我们不再让方点维护与其相连的圆点最小值,而是让其维护子节点(树链剖分中 dfs1 所决定出来的父子)中点权最小值。这样每个圆点权值的改变只会影响到其父亲方点的权值。
$\quad $ 那么怎么维护方点点权呢?
$\quad $ 我们可以给每个方点建一棵平衡树(当然是用 \(multiset\) 了😎)。在改点权时,若是方点,就直接更改,反之,先在其父亲节点的树中将这个点权删去,再把新的权值插入,再更改方点权值即可。
$\quad $ 还有一个细节,就是若两点的 \(lca\) 是一个方点,那么还要算上其父亲节点的权值(读者自行理解)。
$\quad $ tips :方点的序号都大于圆点序号。
点击查看代码
#define yhl 0
#include"bits/stdc++.h"
using namespace std;
#define ld (x<<1)
#define rd (x<<1|1)
const int N=4e5+100;
int dfn[N],low[N],tot,cnt,sta[N],tp;
int n,m,q,w[N],x,y;
int dep[N],top[N],fa[N],son[N],size[N],rnk[N];
multiset<int>ch[N];
struct stu{
int l,r,mi;
}s[N<<2];
void push_up(int x){
s[x].mi=min(s[ld].mi,s[rd].mi);
}
void build(int x,int l,int r){
s[x].l=l,s[x].r=r;
if(l==r){
if(rnk[l]>n)s[x].mi=*ch[rnk[l]].begin();
else s[x].mi=w[rnk[l]];
return;
}
int mid=(l+r)>>1;
build(ld,l,mid);build(rd,mid+1,r);
push_up(x);
}
void change(int x,int pos,int val){
if(s[x].l==s[x].r){
if(rnk[s[x].l]<=n&&fa[rnk[s[x].l]]){
multiset<int> ::iterator it=ch[fa[rnk[s[x].l]]].find(s[x].mi);
ch[fa[rnk[s[x].l]]].erase(it);
s[x].mi=val;
ch[fa[rnk[s[x].l]]].insert(val);
change(1,dfn[fa[rnk[s[x].l]]],*ch[fa[rnk[s[x].l]]].begin());
}else s[x].mi=val;
return;
}
int mid=(s[x].l+s[x].r)>>1;
if(pos<=mid)change(ld,pos,val);
else change(rd,pos,val);
push_up(x);
}
int get_min(int x,int l,int r){
if(l<=s[x].l&&s[x].r<=r)return s[x].mi;
int ans=INT_MAX,mid=(s[x].l+s[x].r)>>1;
if(l<=mid)ans=min(ans,get_min(ld,l,r));
if(r>mid)ans=min(ans,get_min(rd,l,r));
return ans;
}
int get_mi(int x,int y){
int ans=INT_MAX;
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans=min(ans,get_min(1,dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
ans=min(ans,get_min(1,dfn[x],dfn[y]));
if(fa[x]>0&&fa[x]<=n)ans=min(ans,get_min(1,dfn[fa[x]],dfn[fa[x]]));
return ans;
}
vector<int>g[N],t[N<<1];
void tarjan(int x){
low[x]=dfn[x]=++tot;
sta[++tp]=x;
for(int y:g[x]){
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]==dfn[x]){
++cnt;
w[cnt]=INT_MAX;
for(int j=yhl;j!=y;--tp){
j=sta[tp];
t[cnt].push_back(j);
t[j].push_back(cnt);
}
t[cnt].push_back(x);
t[x].push_back(cnt);
}
}else low[x]=min(low[x],dfn[y]);
}
}
void dfs1(int x){
size[x]=1;son[x]=-1;
for(int y:t[x]){
if(!dep[y]){
if(x>n)ch[x].insert(w[y]);
dep[y]=dep[x]+1;
fa[y]=x;
dfs1(y);
size[x]+=size[y];
if(son[x]==-1||size[y]>size[son[x]])son[x]=y;
}
}
}
void dfs2(int x,int to){
top[x]=to;dfn[x]=++tot;rnk[tot]=x;
if(son[x]==-1)return;
dfs2(son[x],to);
for(int y:t[x]){
if((y^son[x])&&(y^fa[x]))dfs2(y,y);
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);cnt=n;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
tarjan(1);dep[1]=1;tot=yhl;
memset(dfn,yhl,sizeof dfn);
dfs1(1);dfs2(1,1);
build(1,1,cnt);
while(q--){
char op;int x,y;
scanf(" %s%d%d",&op,&x,&y);
if(op=='C')change(1,dfn[x],y);
else printf("%d\n",get_mi(x,y));
}
return yhl;
}