耳分解与双极定向

耳分解

耳分解能极好地维护连通性问题。

很形象的说法,设最初的子图为 \(G_0\),原图为 \(G_N\),满足 \(G_0\) 是一个边双连通分量。

每一次加一条“耳”,由图 \(G_i\) 转到图 \(G_{i+1}\)。“耳”就是一条形如 \((x_1 \to x_2 \to x_3...\to x_k)\) 的路径,满足点 \(x_1,x_k \in G_i\)\(\forall_{1<i<k},x_i \notin G_i\)

我们称 \(x_1 \neq x_k\) 的“耳”为开耳。

结论:

  1. 边双必然存在耳分解
  2. 点双必然存在开耳分解

证明只需在 dfs 树上操作即可得证。

例题:P5776(洛谷)


双极定向

一个问题:一张无向图 \(G = (V,E)\),将边定向得到一个 DAG(有向无环图),满足无入度的点只有一个,无出度的点也只有一个。

先考虑有解的条件。

我们先求出圆方树,可以得到:若方点连成一条链则定然有解。

构造我们从拓扑序入手,容易发现对于割点,一定是这个点双中拓扑序最后面的,于是我们以点双中在拓扑序中第一个出现的点为根建出 dfs 树。

然后我们考虑 tarjan 算法,对每个点维护两条边,一条连向父亲 \(f(x)\),一条连向深度最浅的与其有边的点 \(low(x)\)

于是我们就从下往上考虑,对于每个点 \(x\) 用 vector 维护一个后继列表 \(L_x\),每次删一个叶子 \(u\),就在 \(low(x)\)\(f(x)\) 处加入点 \(u\),最后再 dfs 按顺序递归即可得到一组合法拓扑序。

例题:P9394(洛谷)

点击查看代码
#include<bits/stdc++.h>
#define fir first
#define sec second
#define int long long
#define lowbit(x) x&(-x)
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef pair<int,int> pir;
inline int read(){
	int x=0,f=1; char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1; c=getchar();}
	while(isdigit(c)){x=x*10+(c^48); c=getchar();}
	return x*f;
}
const int inf=1e9,N=5e5+5;
int n,m,nd;
vector<int> ed[N],re[N];
int dfn[N],low[N],cnt;
int st[N],top;
inline void tarjan(int x){
    st[++top]=x;
    dfn[x]=low[x]=++cnt;
    for(auto v:ed[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
            if(dfn[x]==low[v]){
                nd++;
                re[nd].push_back(x);
                re[x].push_back(nd);
                for(;st[top+1]!=v;top--){
                    int p=st[top];
                    re[nd].push_back(p);
                    re[p].push_back(nd);
                }
            }
        }
        else low[x]=min(low[x],dfn[v]);
    }
}
int siz[N],f[N],lf[N],ans;
int S,T;
inline void pre(int x,int fa){
    siz[x]=(x<=n);
    int mx=0,sec=0;
    for(auto v:re[x]){
        if(v==fa) continue;
        pre(v,x);
        siz[x]+=siz[v];
        if(siz[mx]<siz[v]) sec=mx,mx=v;
        else if(siz[sec]<siz[v]) sec=v;
    }
    int res=0;
    if(x<=n){
        res=max(res,n-siz[mx]-siz[sec]);
        f[x]=siz[x]-siz[mx];
    }
    else{
        for(auto v:re[x]){
            if(v==fa||v==mx||v==sec) continue;
            res=max(res,siz[v]);
        }
        f[x]=max(res,siz[sec]);
        res=max(res,n-siz[x]);
    }
    res=max({res,f[mx],f[sec]});
    f[x]=max(f[x],f[mx]);
    lf[x]=(mx?lf[mx]:x);
    if(res<ans){
        ans=res;
        if(mx) S=lf[mx];
        else S=x;
        if(sec) T=lf[sec];
        else T=x;
    }
}
int Fa[N];
int Dfn[N],sz[N],id[N],tot;
inline void dfs(int x,int fa){
    Fa[x]=fa,sz[x]=1;
    Dfn[x]=++tot,id[tot]=x;
    for(auto v:re[x]){
        if(v==fa) continue;
        dfs(v,x);
        sz[x]+=sz[v];
    }
}
int dep[N],ff[N],vis[N],Low[N];
int mxdep;
int vv[N],pt[N];
vector<int> D[N],ad[N];
vector<vector<int> > res;
int P[N];
inline void init(int x,int fa){
    ff[x]=fa;
    vis[x]=1;
    dep[x]=dep[fa]+1;
    mxdep=max(mxdep,dep[x]);
    for(auto v:ed[x]){
        if(v==fa||!vv[v]) continue;
        if(vis[v]){
            if(dep[v]<dep[x]){
                if(!Low[x]||dep[Low[x]]>dep[v]) Low[x]=v; 
            }
            continue;
        }
        init(v,x);
        if(!Low[x]||dep[Low[x]]>dep[Low[v]]) Low[x]=Low[v];
    }
    D[dep[x]].push_back(x);
}

inline void gtV(int x){
    vector<int> seq;
    if(!pt[x]){
        for(int i=Dfn[x];i<Dfn[x]+sz[x];i++){
            if(id[i]<=n)
            seq.push_back(id[i]);
        }    
    }
    else{
        seq.push_back(x);
        for(auto v:re[x]){
            if(pt[v]) continue;
            for(int i=Dfn[v];i<Dfn[v]+sz[v];i++)
            if(id[i]<=n) seq.push_back(id[i]);
        }
    }
    res.push_back(seq);
}
inline void dfs2(int x){
    gtV(x);
    vis[x]=1;
    for(auto v:ad[x]) if(!vis[v]) dfs2(v);
    ad[x].clear();
}
inline void solve(int now,int st,int ed){
    mxdep=0;
    for(auto v:re[now]) vv[v]=1,vis[v]=0;
    init(st,0);
    vector<int> seq;
    for(int t=ed;t;t=ff[t]) vis[t]=0,seq.push_back(t);
    reverse(seq.begin(),seq.end());
    for(int i=mxdep;i>=1;i--){
        for(auto x:D[i]){
            if(!vis[x]) continue;
            vis[x]=0;
            if(ff[x]) ad[ff[x]].push_back(x);
            if(Low[x]) ad[Low[x]].push_back(x);
        }
        D[i].clear();
    }
    for(int t=ed;t;t=ff[t]) vis[t]=1;
    for(auto x:seq){
        if(x==st){
            for(auto v:ad[x]) if(!vis[v]) dfs2(v);
            ad[x].clear();
        }
        else dfs2(x);
    }
    for(auto v:re[now]) vv[v]=0;
}

signed main(){
    nd=n=read(),m=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        ed[u].push_back(v);
        ed[v].push_back(u);
    }
    tarjan(1);
    ans=n,pre(1,0);
    dfs(S,0);
    gtV(T);
    int lst=0;
    for(int now=T;now;now=Fa[now]) pt[now]=1;
    for(int now=T;now;now=Fa[now]){
        if(now>n) solve(now,lst,Fa[now]);
        lst=now;
    }
    int Mx=0;
    cout<<ans<<' '<<res.size()<<'\n';
    for(auto tmp:res){
        cout<<tmp.size()<<' ';
        Mx=max(Mx,(int)tmp.size());
        for(auto v:tmp) cout<<v<<' ';
        puts("");
    }
}
</details>
posted @ 2024-12-30 16:06  ~Cyan~  阅读(186)  评论(0)    收藏  举报