CF487E Tourists
CF487E Tourists
前排膜拜T神
上面的话和这道题一点关系都没有
题意
给一个点带权的无向图,每次询问查询两点间所有简单路径上最小值的最小值,单点修改。
思路
一眼圆方树。
——@gxy001
这种题只有在树上做才比较好处理这么多次询问。考虑广义圆方树,缩完点双连通分量后建立的方点和圆点。
因为我们找的是最小值,所以必须将代表整个点双的方点的权值设为与其相连的所有圆点的权值的最小值。用一个 multiset 或者一个可删堆(或者堆带懒惰删除)维护即可。
这样我们就可以在树上使用树剖进行查询了。
但是这题带修改。也就是说,按照上面的方法,更改一个圆点就必须遍历与其相连的所有方点进行修改。这样会被菊花图卡死。于是我们用一个经典套路:方点只维护其儿子的权值。这样我们查询时方法相同,只不过如果求出的 LCA 为方点的话需要再考虑一下其父亲节点的贡献。
由于我们需要区间查询加修改,我们可以将圆方树树剖并用线段树维护最小值。
代码
如果您追求更快速的代码体验,建议使用手写堆+惰性删除。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=2e5+10,INF=0x3f3f3f3f;
int n,m,Q,a[maxn],cnt;
struct gragh{
int ecnt,head[maxn],to[maxn<<1],nxt[maxn<<1];
inline void addedge(int a,int b){
to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt;
to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt;
}
}G1,G2;
int st[maxn],dfn[maxn],tot,id[maxn];
struct SegmentTree{
#define ls (ro<<1)
#define rs (ro<<1|1)
#define mid ((l+r)>>1)
int mn[maxn<<2];
inline void pushup(const int &ro){
mn[ro]=min(mn[ls],mn[rs]);
}
void build(const int& ro=1,const int &l=1,const int &r=cnt){
if(l==r) return mn[ro]=a[id[l]],void();
build(ls,l,mid);build(rs,mid+1,r);
pushup(ro);
}
void update(const int &x,const int &ro=1,const int &l=1,const int &r=cnt){
if(l==r) return mn[ro]=a[id[l]],void();
if(x<=mid) update(x,ls,l,mid);
else update(x,rs,mid+1,r);
pushup(ro);
}
int query(const int &x,const int &y,const int &ro=1,const int &l=1,const int &r=cnt){
if(x==l and y==r) return mn[ro];
if(y<=mid) return query(x,y,ls,l,mid);
else if(x>mid) return query(x,y,rs,mid+1,r);
else return min(query(x,mid,ls,l,mid),query(mid+1,y,rs,mid+1,r));
}
#undef ls
#undef rs
#undef mid
}T;
void tarjan(int x){
dfn[x]=id[x]=++tot;
st[++st[0]]=x;
for(int i=G1.head[x];i;i=G1.nxt[i]){
int u=G1.to[i];
if(!dfn[u]){
tarjan(u);
id[x]=min(id[x],id[u]);
if(id[u]>=dfn[x]){
cnt++;
int now=-1;
G2.addedge(x,cnt);
while(now^u) now=st[st[0]--],G2.addedge(now,cnt);
}
}else id[x]=min(id[x],dfn[u]);
}
}
int dep[maxn],siz[maxn],fa[maxn],son[maxn],top[maxn];
multiset<int> q[maxn];
void dfs1(int x,int f){
fa[x]=f,dep[x]=dep[f]+1,siz[x]=1;
for(int i=G2.head[x];i;i=G2.nxt[i]){
int u=G2.to[i];
if(u==f)continue;
dfs1(u,x);
if(x>n) q[x-n].insert(a[u]);
siz[x]+=siz[u];
if(siz[u]>siz[son[x]])son[x]=u;
}
if(x>n) a[x]=*q[x-n].begin();
}
void dfs2(int x,int topf){
top[x]=topf,dfn[x]=++tot,id[tot]=x;
if(!son[x])return;
dfs2(son[x],topf);
for(int i=G2.head[x];i;i=G2.nxt[i]){
int u=G2.to[i];
if(u==fa[x] or u==son[x])continue;
dfs2(u,u);
}
}
inline int LCA(int x,int y){
int ans=INF;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=min(ans,T.query(dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
ans=min(ans,T.query(dfn[y],dfn[x]));
if(y>n) ans=min(ans,a[fa[y]]);
return ans;
}
inline bool gc(){
char c=getchar();
while(!isalpha(c))c=getchar();
return c=='C';
}
inline void work(){
n=cnt=read(),m=read(),Q=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=m;i++) G1.addedge(read(),read());
tarjan(1);
tot=0;
dfs1(1,0);
dfs2(1,1);
T.build();
while(Q--)
if(gc()){
int x=read(),w=read(),u=fa[x];
if(u)
q[u-n].erase(q[u-n].find(a[x])),
q[u-n].insert(w),
a[u]=*q[u-n].begin(),
T.update(dfn[u]);
a[x]=w;
T.update(dfn[x]);
}else printf("%d\n",LCA(read(),read()));
}
}
signed main(){
star::work();
return 0;
}