题目
题目链接:https://codeforces.com/problemset/problem/487/E
Cyberland 有 n 座城市,编号从 1 到 n,有 m 条双向道路连接这些城市。第 j 条路连接城市 aj 和 bj。每天,都有成千上万的游客来到 Cyberland 游玩。
在每一个城市,都有纪念品售卖,第 i 个城市售价为 wi。这个售价有时会变动。
每一个游客的游览路径都有固定起始城市和终止城市,且不会经过重复的城市。
他们会在路径上的城市中,售价最低的那个城市购买纪念品。
你能求出每一个游客在所有合法的路径中能购买的最低售价是多少吗?
你要处理 q 个操作:
C a w: 表示 a 城市的纪念品售价变成 w。
A a b: 表示有一个游客要从 a 城市到 b 城市,你要回答在所有他的旅行路径中最低售价的最低可能值。
(n,m,qleq 10^5,w_ileq 10^9)。
思路
图上路径问题不经脑子直接上圆方树(
那么显然只需要把每个方点的权值设为它表示的点双的权值最小值即可,具体可以用 multiset 维护,然后路径最小值直接上树剖+线段树。
但是我们发现我们如果修改一个点 (x) 的权值,那么 (x) 所在点双的 multiset 都要维护,但是 (x) 所在点双数量是 (O(n)) 的,无法保证复杂度。
所以我们考虑 multiset 中只记录方点儿子的权值,这样修改的时候就只需要维护一个 multiset 了。同时如果询问时 (x,y) 两点的 LCA 是方点,那么就在单独拎出他们 LCA 的父亲(显然是圆点)的贡献取最小值即可。
时间复杂度 (O(nlog^2 n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=200010,Inf=2e9;
int n,m,Q,cnt,a[N];
multiset<int> s[N];
struct edge
{
int next,to;
};
struct SegTree
{
int minn[N*4];
void update(int x,int l,int r,int k,int v)
{
if (l==r) { minn[x]=v; return; }
int mid=(l+r)>>1;
if (k<=mid) update(x*2,l,mid,k,v);
else update(x*2+1,mid+1,r,k,v);
minn[x]=min(minn[x*2],minn[x*2+1]);
}
int query(int x,int l,int r,int ql,int qr)
{
if (ql<=l && qr>=r) return minn[x];
int mid=(l+r)>>1,mn=Inf;
if (ql<=mid) mn=min(mn,query(x*2,l,mid,ql,qr));
if (qr>mid) mn=min(mn,query(x*2+1,mid+1,r,ql,qr));
return mn;
}
}seg;
struct woshishabi
{
int tot,head[N],dep[N],top[N],son[N],fa[N],id[N],siz[N];
edge e[N*2];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
void dfs1(int x,int f)
{
dep[x]=dep[f]+1; fa[x]=f; siz[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=f)
{
dfs1(v,x);
siz[x]+=siz[v];
if (siz[v]>siz[son[x]]) son[x]=v;
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp; id[x]=++tot;
if (son[x]) dfs2(son[x],tp);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa[x] && v!=son[x]) dfs2(v,v);
}
}
int lca(int x,int y)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if (dep[x]<dep[y]) swap(x,y);
return y;
}
int query(int x,int y)
{
int res=Inf;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
res=min(res,seg.query(1,1,cnt,id[top[x]],id[x]));
x=fa[top[x]];
}
if (dep[x]<dep[y]) swap(x,y);
res=min(res,seg.query(1,1,cnt,id[y],id[x]));
return res;
}
}T;
struct wotaicaile
{
int tot,head[N],dfn[N],low[N];
edge e[N*2];
stack<int> st;
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
st.push(x);
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);
if (low[v]>=dfn[x])
{
int y; cnt++;
do {
y=st.top(); st.pop();
T.add(y,cnt); T.add(cnt,y);
} while (y!=v);
T.add(x,cnt); T.add(cnt,x);
}
}
else low[x]=min(low[x],dfn[v]);
}
}
}G;
int main()
{
memset(T.head,-1,sizeof(T.head));
memset(G.head,-1,sizeof(G.head));
scanf("%d%d%d",&n,&m,&Q);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
G.add(x,y); G.add(y,x);
}
G.tot=0; cnt=n;
G.tarjan(1);
T.tot=0;
T.dfs1(1,0); T.dfs2(1,1);
for (int i=1;i<=n;i++)
{
s[T.fa[i]].insert(a[i]);
seg.update(1,1,cnt,T.id[i],a[i]);
}
for (int i=n+1;i<=cnt;i++)
{
s[i].insert(Inf);
seg.update(1,1,cnt,T.id[i],*s[i].begin());
}
while (Q--)
{
char typ[3]; int x,y;
scanf("%s%d%d",typ,&x,&y);
if (typ[0]=='C')
{
s[T.fa[x]].erase(s[T.fa[x]].find(a[x]));
a[x]=y; s[T.fa[x]].insert(y);
seg.update(1,1,cnt,T.id[T.fa[x]],*s[T.fa[x]].begin());
seg.update(1,1,cnt,T.id[x],y);
}
else
{
int p=T.lca(x,y);
printf("%d
",min(T.query(x,y),((p>n)?a[T.fa[p]]:Inf)));
}
}
return 0;
}