背景:
越来越困了。
题目传送门:
https://www.luogu.org/problem/CF487E
题意:
一个图,支持修改点权,支持查询
x
,
y
x,y
x,y之间所有不重边路径的最小值。
思路:
若这个问题在树上就是一道大水题了,直接树剖维护即可。
但是这个问题在图上,将图变成树的一种操作是圆方树,不妨考虑圆方树来解决。
由于我们知道若有路径经过一个点双,则这个点双对答案的贡献应该为这个点双的最小值,因此我们可以存在方点上,可以用
multiset
\text{multiset}
multiset或线段树来实现。
但是若为一个菊花图这样的修改是的时间复杂度是
Θ
(
n
)
\Theta(n)
Θ(n),总的时间复杂度就为
Θ
(
n
m
)
\Theta(nm)
Θ(nm)的,显然会被卡掉。
考虑如何快速修改。
当前的方点不维护它父亲的贡献。
将一个方点的贡献记录在它的父亲,某一个圆点上面,那么我们每一次修改就只用统计路径上的最小值;若这两个点在圆方树上的
lca
\text{lca}
lca为方点,再对它的父亲求一个最小值即可(因为它的父亲也一定属于这个点双)。
修改的时候在线段树中修改当前的位置,然后在它的父亲(圆点的父亲一定是方点)的
multiset
\text{multiset}
multiset删掉原来的权值,加入新的,然后对应的线段树修改即可。
这个仔细思考一下就可以了。
代码实现也不难。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ID multiset<int>::iterator
using namespace std;
multiset<int> F[400010];
int n,m,q,op,len=0,lena=0,lenb=0;
int val[400010],lasta[400010],lastb[400010],sta[400010],dfn[400010],low[400010];
int top[400010],dep[400010],size[400010],fa[400010],son[400010],id[400010];
struct node1{int x,y,next;} a[800010],b[800010];
struct node2{int l,r,lc,rc,mi;} tr[800010];
void insa(int x,int y)
{
a[++lena]=(node1){x,y,lasta[x]}; lasta[x]=lena;
}
void insb(int x,int y)
{
b[++lenb]=(node1){x,y,lastb[x]}; lastb[x]=lenb;
}
int TOP=0,cnt=0;
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
sta[++TOP]=x;
for(int i=lastb[x];i;i=b[i].next)
{
int y=b[i].y;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
op++,insa(op,x),insa(x,op);
int tmp;
do
{
tmp=sta[TOP--];
insa(op,tmp),insa(tmp,op);
} while(tmp!=y);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
void dfs1(int x)
{
size[x]=1;
for(int i=lasta[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa[x]) continue;
dep[y]=dep[x]+1;
fa[y]=x;
if(x>n) F[x].insert(val[y]);
dfs1(y);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
int cntt=0;
void dfs2(int x,int tp)
{
id[x]=++cntt;
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=lasta[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
void build(int l,int r)
{
int now=++len;
tr[now]=(node2){l,r,-1,-1,2147483647};
if(l<r)
{
int mid=(l+r)>>1;
tr[now].lc=len+1; build(l,mid);
tr[now].rc=len+1; build(mid+1,r);
}
}
void change(int now,int x,int d)
{
if(tr[now].l==tr[now].r)
{
tr[now].mi=d;
return;
}
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(x<=mid) change(lc,x,d); else change(rc,x,d);
tr[now].mi=min(tr[lc].mi,tr[rc].mi);
}
int findmin(int now,int l,int r)
{
if(tr[now].l==l&&tr[now].r==r) return tr[now].mi;
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(r<=mid) return findmin(lc,l,r);
else if(l>mid) return findmin(rc,l,r);
else return min(findmin(lc,l,mid),findmin(rc,mid+1,r));
}
int solve(int x,int y)
{
int tx=top[x],ty=top[y];
int mi=2147483647;
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(x,y),swap(tx,ty);
mi=min(mi,findmin(1,id[ty],id[y]));
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y);
mi=min(mi,findmin(1,id[x],id[y]));
if(x>n) mi=min(mi,val[fa[x]]);
return mi;
}
int main()
{
char s[10];
int x,y;
scanf("%d %d %d",&n,&m,&q);
for(int i=1;i<=n;i++)
scanf("%d",&val[i]);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&x,&y);
insb(x,y),insb(y,x);
}
op=n;
tarjan(1);
dfs1(1);
dfs2(1,1);
build(1,op);
for(int i=1;i<=n;i++)
change(1,id[i],val[i]);
for(int i=n+1;i<=op;i++)
change(1,id[i],*F[i].begin());
for(int i=1;i<=q;i++)
{
scanf("%s %d %d",s+1,&x,&y);
if(s[1]=='C')
{
if(fa[x]) F[fa[x]].erase(F[fa[x]].lower_bound(val[x])),F[fa[x]].insert(y),change(1,id[fa[x]],*F[fa[x]].begin());
val[x]=y,change(1,id[x],val[x]);
}
else
{
printf("%d\n",solve(x,y));
}
}
}