題目大意
給你 個點和 條邊的無向圖,沒有自環,沒有重邊,每個點上面有點權。
每次可能有兩種操作:修改一個點的點權,或者詢問兩個點之間的路徑上最小可能的點權是多少。
想法
有一個很顯然的貪心想法,詢問的時候肯定優先走較小權值路徑,也就是在有分叉(點雙)的時候走較小權值的那一側,而只有可能最小的會造成貢獻。
所以說我們可以嘗試將每個點雙之間建一個堆,記錄最小的點權。
也就是建出一棵圓方樹,方點上面一個堆,記錄與他向連的圓點的權值最小值。
每次詢問就只需要考慮經過唯一路徑上面 圓點點權 和 方點堆頂 的最小值就行了,這個可以用樹鏈剖分搞。
然後考慮修改,便是修改與這個 圓點 相連的方點上面的堆就好了,刪除原來的點權,加入新點權。但是這樣子我們不能保證其複雜度,要是圓方樹建成一個菊花圖就會 飛了。
我們再考慮一下,我們樹鏈剖分往上跳的時候,其實已經計算過 走過的方點 的父親了(圓點),會有計算重複的地方。所以說 方點的堆 裏面不需要記錄其父親的權值,換句話說,對於一個圓點,他的值只需要被他的 方點父親 的堆記錄,這樣子每次修改,只需要修改他父親的,加上線段樹和堆的複雜度也不過一次 。
但是每次詢問的是兩個圓點,其有可能是方點,這個時候少計算了一個 方點 的父親,需要注意加入判斷一下。(第一個樣第一次例詢問輸出爲2有可能就是這樣死的)
然後就是 圓方樹+樹鏈剖分+線段樹+可刪堆 套模板了,可刪堆我是用 實現的,所以說代碼比較醜。
預處理複雜度,修改操作複雜度,詢問操作複雜度。
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<map>
#include<set>
using namespace std;
const int inf=0x3f3f3f3f;
const double eps=1e-10;
const double pi=acos(-1.0);
//char buf[1<<15],*S=buf,*T=buf;
//char getch(){return S==T&&(T=(S=buf)+fread(buf,1,1<<15,stdin),S==T)?0:*S++;}
inline int read(){
int x=0,f=1;char ch;ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=0;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch&15);ch=getchar();}
if(f)return x;else return -x;
}
const int N=4e5+200;
vector<int> G[N],E[N];
void add_E(int x,int y){E[x].push_back(y);E[y].push_back(x);}
void add_G(int x,int y){G[x].push_back(y);G[y].push_back(x);}
int sta[N],top,Size;
int tim,dfn[N],low[N];
void tarjan(int u){
dfn[u]=low[u]=++tim;
sta[++top]=u;
for(auto v:E[u]){
if(dfn[v]) low[u]=min(low[u],dfn[v]);
else{
tarjan(v),low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
++Size;int p;
while((p=sta[top])!=v)add_G(p,Size),top--;
p=sta[top],add_G(p,Size),top--;
add_G(u,Size);
}
}
}
}
struct node{int ls,rs,key,size,val;}T[N<<1];
#define lson T[rt].ls
#define rson T[rt].rs
int node_cnt;
queue<int>Rub;
int newnode(int x){
int rt;
if(!Rub.empty())rt=Rub.front(),Rub.pop();
else rt=++node_cnt;
lson=rson=0;
T[rt].key=rand();T[rt].val=x;T[rt].size=1;
return rt;
}
struct fhq_Treap{
int root;
void pushup(int rt){T[rt].size=T[lson].size+T[rson].size+1;}
int merge(int a,int b){
if(!a||!b)return a|b;
if(T[a].key<T[b].key){T[a].rs=merge(T[a].rs,b);pushup(a);return a;}
else{T[b].ls=merge(a,T[b].ls);pushup(b);return b;}
}
void split(int rt,int x,int &a,int &b){
if(!rt){a=b=0;return;}
if(T[rt].val<=x){a=rt;split(rson,x,rson,b);}
else{b=rt;split(lson,x,a,lson);}
pushup(rt);
}
void Insert(int x){
int a,b;
split(root,x,a,b);
int rt=newnode(x);
root=merge(merge(a,rt),b);
}
void Delete(int x){
int a,b,c;
split(root,x,a,c);
split(a,x-1,a,b);
b=merge(T[b].ls,T[b].rs);
root=merge(merge(a,b),c);
}
int Min(){
int rt=root;
while(lson)rt=lson;
return T[rt].val;
}
}Treap[N];
int pos[N],w[N];
struct Segment_Tree{
#define Lson l,mid,rt<<1
#define Rson mid+1,r,rt<<1|1
int sum[N<<2];
void pushup(int rt){sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);}
void build(int l,int r,int rt){
if(l==r){sum[rt]=w[pos[l]];return;}
int mid=(l+r)>>1;
build(Lson);build(Rson);
pushup(rt);
}
void Update(int id,int c,int l,int r,int rt){
if(l==r){sum[rt]=c;return;}
int mid=(l+r)>>1;
if(id<=mid)Update(id,c,Lson);
else Update(id,c,Rson);
pushup(rt);
}
int Query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R)return sum[rt];
int ret=1e9,mid=(l+r)>>1;
if(L<=mid)ret=min(ret,Query(L,R,Lson));
if(R>mid)ret=min(ret,Query(L,R,Rson));
return ret;
}
}Seg;
int n,m,Q;
struct Tree_Chain_Dissection{
int idx[N];
int deep[N],fa[N],son[N],size[N];
int cnt,top[N];
int dfs1(int u,int f,int dep){
deep[u]=dep;fa[u]=f;size[u]=1;
Treap[fa[u]].Insert(w[u]);
int maxson=-1;
for(auto v:G[u])if(v!=f){
size[u]+=dfs1(v,u,dep+1);
if(size[v]>maxson)maxson=size[v],son[u]=v;
}
return size[u];
}
void dfs2(int u,int topf){
idx[u]=++cnt;top[u]=topf;
pos[cnt]=u;
if(!son[u])return;
dfs2(son[u],topf);
for(auto v:G[u])if(!idx[v])dfs2(v,v);
}
void init(){
dfs1(1,0,1);
dfs2(1,1);
for(int i=n+1;i<=Size;++i)w[i]=Treap[i].Min();
Seg.build(1,Size,1);
}
void Update(int u,int val){
if(fa[u]){
Treap[fa[u]].Delete(w[u]);
Treap[fa[u]].Insert(val);
Seg.Update(idx[fa[u]],Treap[fa[u]].Min(),1,Size,1);
}
w[u]=val;
Seg.Update(idx[u],val,1,Size,1);
}
int Query(int x,int y){
int ans=inf;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
ans=min(ans,Seg.Query(idx[top[x]],idx[x],1,Size,1));
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
ans=min(ans,Seg.Query(idx[x],idx[y],1,Size,1));
if(x<=n)return ans;
else return min(ans,w[fa[x]]);
}
}TCD;
char op[2];
int a,b;
int main()
{
srand(time(NULL));
Size=n=read();m=read();Q=read();w[0]=inf;
for(int i=1;i<=n;++i)w[i]=read();
for(int i=1,a,b;i<=m;++i)
a=read(),b=read(),add_E(a,b);
tarjan(1);
TCD.init();
while(Q--){
scanf("%s",op);a=read(),b=read();
if(op[0]=='C')TCD.Update(a,b);
if(op[0]=='A')printf("%d\n",TCD.Query(a,b));
}
return 0;
}