耳分解与双极定向
耳分解
耳分解能极好地维护连通性问题。
很形象的说法,设最初的子图为
每一次加一条“耳”,由图
我们称
结论:
- 边双必然存在耳分解
- 点双必然存在开耳分解
证明只需在 dfs 树上操作即可得证。
例题:P5776(洛谷)
双极定向
一个问题:一张无向图
先考虑有解的条件。
我们先求出圆方树,可以得到:若方点连成一条链则定然有解。
构造我们从拓扑序入手,容易发现对于割点,一定是这个点双中拓扑序最后面的,于是我们以点双中在拓扑序中第一个出现的点为根建出 dfs 树。
然后我们考虑 tarjan 算法,对每个点维护两条边,一条连向父亲
于是我们就从下往上考虑,对于每个点
例题: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>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现