CF487E Tourists
CF487E Tourists
圆方树
本题中,很容易考虑到建成圆方树,把所有点双内的节点最小值作为方点的权值,然后求链上最小值即可。
这样做的正确性显然,但是难以维护修改操作。
我们每次修改一个节点,都需要修改其相邻的方点,这样的复杂度是无法接受的。
我们考虑换一个方式,我们钦定一个叶子节点为圆方树的根后,对于方点,我们只记录其儿子节点的\(\min\),那么修改只需要修改父亲方点。
而求链最小值时,我们发现,除了两个节点的\(lca\)为方点的情况,我们求出的链最小值仍然是对的。那么我们只需要判断一下是否是方点,如果是,把其父亲统计入答案即可。
\(lca\)和链信息维护,一个\(LCT\)就完事。
对于方点的维护,可以用\(multiset\)或可删堆。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#define N 100005
#define M 200005
using namespace std;
const int INF=1000000007;
stack<int>s;
char opt[5];
int n,m,Q,x,y,ct,rt=1,w[M];
int o[N];
struct edge
{
int nxt,v;
edge (int Nxt=0,int V=0)
{
nxt=Nxt,v=V;
}
}e[N << 1];
int F[M];
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
#define fa(x) a[x].f
#define val(x) a[x].value
#define rev(x) a[x].tag
struct DelHeap
{
priority_queue<int,vector<int>,greater<int> >q1,q2;
int top()
{
while (!q2.empty() && q1.top()==q2.top())
q1.pop(),q2.pop();
return q1.top();
}
void pop()
{
while (!q2.empty() && q1.top()==q2.top())
q1.pop(),q2.pop();
q1.pop();
}
void push(int x)
{
q1.push(x);
}
void erase(int x)
{
q2.push(x);
}
}g[M];
struct Link_Cut_Tree
{
int ch[2],f,value;
bool tag;
}a[M];
int q[M];
int id(int x)
{
return ls(fa(x))==x?0:1;
}
bool isrt(int x)
{
return ls(fa(x))!=x && rs(fa(x))!=x;
}
void connect(int x,int F,int son)
{
fa(x)=F;
a[F].ch[son]=x;
}
void update(int x)
{
val(x)=min(w[x],min(val(ls(x)),val(rs(x))));
}
void rot(int x)
{
int y=fa(x),r=fa(y);
int yson=id(x),rson=id(y);
if (isrt(y))
fa(x)=r; else
connect(x,r,rson);
connect(a[x].ch[yson^1],y,yson);
connect(y,x,yson^1);
update(y),update(x);
}
void push_rev(int x)
{
if (!x)
return;
swap(ls(x),rs(x));
rev(x)=!rev(x);
}
void push_down(int x)
{
if (rev(x))
{
push_rev(ls(x));
push_rev(rs(x));
rev(x)=0;
}
}
void splay(int x)
{
int g=x,k=0;
q[++k]=x;
while (!isrt(g))
g=fa(g),q[++k]=g;
while (k)
push_down(q[k--]);
while (!isrt(x))
{
int y=fa(x);
if (isrt(y))
rot(x); else
if (id(x)==id(y))
rot(y),rot(x); else
rot(x),rot(x);
}
}
void access(int x)
{
for (int y=0;x;y=x,x=fa(x))
{
splay(x);
rs(x)=y;
update(x);
}
}
void makeroot(int x)
{
access(x);
splay(x);
push_rev(x);
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
makeroot(x);
fa(x)=y;
}
int lca(int x,int y)
{
makeroot(rt);
access(y);
int t=0;
for (;x;t=x,x=fa(x))
{
splay(x);
rs(x)=t;
update(x);
}
return t;
}
int tot,fr[N];
int cnt=0,dfn[N],low[N];
void add(int x,int y)
{
++tot;
e[tot]=edge(fr[x],y),fr[x]=tot;
}
void add_edge(int x,int y)
{
add(x,y),add(y,x);
}
void Tarjan(int u)
{
dfn[u]=low[u]=++cnt;
s.push(u);
for (int i=fr[u];i;i=e[i].nxt)
{
int v=e[i].v;
if (!dfn[v])
{
Tarjan(v);
if (low[v]==dfn[u])
{
++ct;
F[ct]=u;
o[0]=0;
int t;
do
{
t=s.top();
s.pop();
g[ct].push(w[t]);
o[++o[0]]=t;
F[t]=ct;
} while (t!=v);
o[++o[0]]=u;
w[ct]=g[ct].top();
val(ct)=w[ct];
for (int j=1;j<=o[0];++j)
link(ct,o[j]);
}
low[u]=min(low[u],low[v]);
} else
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
val(0)=INF;
scanf("%d%d%d",&n,&m,&Q),ct=n;
for (int i=1;i<=n;++i)
scanf("%d",&w[i]),val(i)=w[i];
for (int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
add_edge(x,y);
}
Tarjan(1);
while (Q--)
{
scanf("%s%d%d",opt,&x,&y);
if (opt[0]=='C')
{
int s=F[x];
if (s)
{
g[s].erase(w[x]);
g[s].push(y);
splay(s);
w[s]=g[s].top();
update(s);
}
splay(x);
w[x]=y;
update(x);
} else
{
split(x,y);
int ans=val(y);
int s=lca(x,y);
if (s>n)
ans=min(ans,w[F[s]]);
printf("%d\n",ans);
}
}
return 0;
}