CF487E Tourists 圓方樹 樹剖 multiset
阿新 • • 發佈:2018-11-23
題目連結 題目連結是洛谷上有翻譯的。
題目描述:
給你一張n個點m條邊的圖,每個點有點權,我們有q次操作,有兩種操作,第一種是把一個點的點權變成另一個點的點權,第二種是詢問兩點之間可能走過的簡單路徑中的所有點的最小點權。n,m,q都是1e5量級。
題解:
遇到這種圖上簡單路徑問題還是考慮建出圓方樹。這個題點有點權,那麼圓點的點權就是本來的點權,我們來考慮方點的點權應該表示什麼。一般來講,方點的權值是它表示的點雙的資訊和或者是不算在圓方樹上它父節點的那個圓點之外的資訊和。這個題如果是想表示整個點雙的資訊和的話你修改一個點的點權時,由於這個點可能位於多個點雙,所以你可能會需要修改多個方點的資訊,這種做法是會被菊花圖卡成
的,因為菊花的中心連線著
級別的方點。
那麼我們只能選擇第二種方法,我們在每個方點記錄它所表示的點雙除了圓方樹上這個方點的父節點之外的所有點的資訊,在這個題是權值的min,在修改的時候我們只用圓點來更新它父節點所在的方點的資訊。但是有一個問題是如果原來這個圓點是它父節點的方點所表示的所有點中點權最小的,但是修改之後它的點權可能不一定還是最小的了,所以我們還要再次快速找到修改後最小的點權。於是我們的方法是對於每個方點用一個multiset維護,維護的東西是它表示的所有圓點的權值。每次修改相當於刪去原來的權值,加進去一個新的權值。然後再用這個最小值去更新線段樹上dfs序對應點的權值。
對於詢問,我們要找的路徑是兩點在圓方樹上的路徑。當然,還是要用到圓方樹常見的套路:在LCA處分圓點和方點討論。如果LCA是圓點,那麼照常求,如果是方點,那麼這個方點的父節點也可能成為答案要求的路徑中的點,於是也要取個min。求樹上一條路徑的最小點權就是用樹剖了。
程式碼:
#include <bits/stdc++.h>
using namespace std;
int n,m,hed[200010],cnt,hed2[200010],cnt2,q,w[200010],ct;
int low[200010],dfn[200010],sta[200010],top,z;
int tp[200010],sz[200010],fa[200010],dep[200010],son[200010],ys[200010],yss[200010];
struct node
{
int to,next;
}a[400010],aa[800010];
struct tree
{
int l,r,mn;
}tr[2000010];
char opt;
multiset<int> s[400010];
inline void add(int from,int to)
{
a[++cnt].to=to;
a[cnt].next=hed[from];
hed[from]=cnt;
}
inline void add2(int from,int to)
{
aa[++cnt2].to=to;
aa[cnt2].next=hed2[from];
hed2[from]=cnt2;
}
inline void tarjan(int x)
{
low[x]=dfn[x]=++z;
sta[++top]=x;
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x])
{
++ct;
int mn=2e9;
do
{
add2(ct,sta[top]);
add2(sta[top],ct);
--top;
}while(sta[top+1]!=y);
add2(x,ct);
add2(ct,x);
mn=min(mn,w[x]);
w[ct]=mn;
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
inline void dfs(int x)
{
sz[x]=1;
for(int i=hed2[x];i;i=aa[i].next)
{
int y=aa[i].to;
if(y==fa[x])
continue;
fa[y]=x;
dep[y]=dep[x]+1;
dfs(y);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]])
son[x]=y;
}
}
inline void dfs2(int x,int top)
{
tp[x]=top;
ys[x]=++z;
yss[z]=x;
if(son[x])
{
dfs2(son[x],top);
for(int i=hed2[x];i;i=aa[i].next)
{
int y=aa[i].to;
if(y!=fa[x]&&y!=son[x])
dfs2(y,y);
}
}
}
inline void pushup(int rt)
{
tr[rt].mn=min(tr[rt<<1].mn,tr[rt<<1|1].mn);
}
inline void build(int rt,int l,int r)
{
tr[rt].l=l;
tr[rt].r=r;
if(l==r)
{
tr[rt].mn=w[yss[l]];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
inline void update(int rt,int x,int y)
{
int l=tr[rt].l,r=tr[rt].r;
if(l==r)
{
tr[rt].mn=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid)
update(rt<<1,x,y);
else
update(rt<<1|1,x,y);
pushup(rt);
}
inline int query(int rt,int le,int ri)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&r<=ri)
return tr[rt].mn;
int mid=(l+r)>>1,res=2e9;
if(le<=mid)
res=min(res,query(rt<<1,le,ri));
if(mid+1<=ri)
res=min(res,query(rt<<1|1,le,ri));
return res;
}
inline int find(int x,int y)
{
int fx=tp[x],fy=tp[y],ans=2e9;
while(fx!=fy)
{
if(dep[fx]<dep[fy])
{
swap(x,y);
swap(fx,fy);
}
ans=min(ans,query(1,ys[fx],ys[x]));
x=fa[fx];
fx=tp[x];
}
if(dep[x]>dep[y])
swap(x,y);
ans=min(ans,query(1,ys[x],ys[y]));
if(x>ct)
ans=min(ans,w[fa[x]]);
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;++i)
scanf("%d",&w[i]);
ct=n;
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=n;++i)
{
if(!dfn[i])
tarjan(i);
}
z=0;
dep[1]=1;
dfs(1);
dfs2(1,1);
for(int i=n+1;i<=ct;++i)
{
for(int j=hed2[i];j;j=aa[j].next)
{
int y=aa[j].to;
if(y!=fa[i])
s[i].insert(w[y]);
}
if(s[i].empty())
w[i]=2e9;
else
w[i]=*s[i].begin();
}
swap(n,ct);
build(1,1,n);
for(int i=1;i<=q;++i)
{
opt=getchar();
while(opt!='A'&&opt!='C')
opt=getchar();
int x,y;
scanf("%d%d",&x,&y);
if(opt=='A')
printf("%d\n",find(x,y));
else
{
if(fa[x])
{
s[fa[x]].erase(s[fa[x]].find(w[x]));
s[fa[x]].insert(y);
w[fa[x]]=*s[fa[x]].begin();
update(1,ys[fa[x]],w[fa[x]]);
}
w[x]=y;
update(1,ys[x],y);
}
}
return 0;
}