1. 程式人生 > 實用技巧 >題解 CF487E 【Tourists】

題解 CF487E 【Tourists】

若從 \(x\)\(y\) 的任意一條路徑經過了一個點雙連通分量,則從 \(x\)\(y\) 一定可以經過該點雙連通分量中的每一個點。

用廣義圓方樹來維護一般無向圖,每個方點的權值為其相鄰的圓點的權值的最小值,然後可以用樹剖來修改和查詢。

但是這樣修改的複雜度是不正確的,若一個圓點相鄰有許多方點,像菊花圖一樣,那麼複雜度是無法接受的。

考慮更改方點的權值定義,權值改為在圓方樹上的其兒子權值的最小值。這樣修改一個圓點時,只用考慮其父親方點的權值的變化,這樣修改複雜度就正確了。

在每個方點上用 \(multiset\) 來維護其兒子的權值,查詢時兩點的 \(lca\) 若為方點,則還要考慮其父親圓點的貢獻。

\(code:\)

#include<bits/stdc++.h>
#define maxn 400010
#define maxm 1600010
#define inf 2000000000
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,q,tot,dfn_cnt,cnt,root=1;
int val[maxn],dfn[maxn],low[maxn],st[maxn];
int fa[maxn],de[maxn],siz[maxn],son[maxn],top[maxn],rev[maxn],mi[maxm];
vector<int> ve[maxn];
multiset<int> s[maxn];
char opt[5];
struct edge
{
    int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
    e[++edge_cnt]=(edge){to,head[from]};
    head[from]=edge_cnt;
}
void addedge(int x,int y)
{
    ve[x].push_back(y);
}
void tarjan(int x)
{
    dfn[x]=low[x]=++dfn_cnt,st[++cnt]=x;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(!dfn[y])
        {
            tarjan(y),low[x]=min(low[x],low[y]);
            if(dfn[x]<=low[y])
            {
                tot++;
                int now;
                do
                {
                    now=st[cnt--];
                    addedge(tot,now),addedge(now,tot);
                }while(now!=y);
                addedge(tot,x),addedge(x,tot);
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
void dfs_son(int x,int fath)
{
    de[x]=de[fath]+1,siz[x]=1,fa[x]=fath,s[fath].insert(val[x]);
    for(int i=0;i<ve[x].size();++i)
    {
        int  y=ve[x][i];
        if(y==fath) continue;
        dfs_son(y,x),siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
    }
}
void dfs_chain(int x,int tp)
{
    dfn[x]=++dfn_cnt,rev[dfn_cnt]=x,top[x]=tp;
    if(son[x]) dfs_chain(son[x],tp);
    for(int i=0;i<ve[x].size();++i)
    {
        int y=ve[x][i];
        if(dfn[y]) continue;
        dfs_chain(y,y);
    }
}
void build(int l,int r,int cur)
{
    if(l==r)
    {
        int x=rev[l];
        if(x<=n) mi[cur]=val[x];
        else mi[cur]=*s[x].begin();
        return;
    }
    build(l,mid,ls),build(mid+1,r,rs);
    mi[cur]=min(mi[ls],mi[rs]);
}
void modify(int l,int r,int pos,int v,int cur)
{
    if(l==r)
    {
        mi[cur]=v;
        return;
    }
    if(pos<=mid) modify(l,mid,pos,v,ls);
    else modify(mid+1,r,pos,v,rs);
    mi[cur]=min(mi[ls],mi[rs]);
}
int query(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return mi[cur];
    int v=inf;
    if(L<=mid) v=min(v,query(L,R,l,mid,ls));
    if(R>mid) v=min(v,query(L,R,mid+1,r,rs));
    return v;
}
int ask(int x,int y)
{
    int v=inf;
    while(top[x]!=top[y])
    {
        if(de[top[x]]<de[top[y]]) swap(x,y);
        v=min(v,query(dfn[top[x]],dfn[x],1,tot,root));
        x=fa[top[x]];
    }
    if(dfn[x]>dfn[y]) swap(x,y);
    v=min(v,query(dfn[x],dfn[y],1,tot,root));
    if(x>n) v=min(v,val[fa[x]]);
    return v;
}
int main()
{
    read(n),read(m),read(q),tot=n,val[0]=inf;
    for(int i=1;i<=n;++i) read(val[i]);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        read(x),read(y);
        add(x,y),add(y,x);
    }
    tarjan(1),dfn_cnt=0,memset(dfn,0,sizeof(dfn));
    dfs_son(1,0),dfs_chain(1,1),build(1,tot,root);
    while(q--)
    {
        int x,y;
        scanf("%s",opt),read(x),read(y);
        if(opt[0]=='C')
        {
            s[fa[x]].erase(s[fa[x]].find(val[x]));
            s[fa[x]].insert(y),val[x]=y;
            modify(1,tot,dfn[x],y,root);
            modify(1,tot,dfn[fa[x]],*s[fa[x]].begin(),root);
        }
        else printf("%d\n",ask(x,y));
    }
    return 0;
}