带花树
作者:互联网
关于版权意识
参考抄袭自 _rqy 和 Bill_Yang 的 blog 。
这是什么?
带花的树。
什么是花?
在一个 \(M\) 匹配的图中的一个有着 \(2k+1\) 条边且其中 \(k\) 条边在 \(M\) 中的奇环被称作花。要求花上任意一点存在到任意一个孤立点的交错路,该交错路被称作花茎。
有什么用?
解决一般图最大匹配问题!
为什么要引入花的概念?
首先考虑能否用二分图最大匹配的不停找增广路的思想(?)解决一般图最大匹配,答案是不可以。问题在于:在二分图上找增广路时,交替路只会经过链或偶环,而在偶环上走了偶数步之后交替路的当前奇偶状态没有改变,即匹配的合法性不会受到影响。不难发现影响一般图最大匹配的是奇环,这就是说,只要将一般图中的奇环的影响消除,就可以轻松寻找增广路了。
花的出现就是为了将一般图中的奇环缩点,以排除找增广路时绕花一周重新进入花茎造成的干扰。
不难发现,当我们找出了一个合法的奇环,只要这个奇环中的点没有向外匹配,就可以通过调整环内匹配状况使任意一个环外的点可以与环内任意一点匹配。会向外匹配的奇环就是花,因此我们可以将花缩点以消除其影响。
怎么找花?
-
枚举孤立点 \(\omega\)
-
从 \(\omega\) 开始宽搜,将 \(\omega\) 标记成 \(o\),交替地用 \(o(out)\) 和 \(i(in)\) 标记经历的点
-
若两个相邻的点都是 \(o\) ,则找到了一朵花
为什么不考虑两个相邻点都是 \(i\) 的情况?
考虑 \(i\) 和 \(o\) 的意义,从 \(\omega\) 出发的一条 \(i\rightarrow o\) 代表一个对答案有贡献的匹配。当两个 \(i\) 相邻,虽然当前环是一个奇环,但是当前的交错路不会是花茎。这就是说,花根不会与环外匹配,因此不会对答案产生干扰。
此外,在代码实现中,只有 \(o\) 型点会入队,因此相邻的 \(i\) 型点不在队列中,不会影响到增广路的寻找。
怎么找增广路?
约定:
\(pre\): 在上文中,通过交错路的奇偶性寻找增广路,但在代码实现中可以直接记录每个点在交错路中相邻但不匹配的点,即若这个点失配要和谁匹配。在非花的点上, \(pre\) 是单向的。
\(father\): 这个点所属的花,不属于花的点的 \(father\) 就是其自身,用并查集维护。
-
枚举孤立点 \(\omega\) ,一边宽搜一边标记
-
若当前点未被标记且未被匹配,则将其标记为 \(i\) 点,并从其开始进行一个增广,更新维护增广信息。
-
若当前点未被标记但已被匹配,则将其标记为 \(i\) 点,并将其匹配到的点标记为 \(o\) 点,维护增广信息。
-
若当前点已被标记为 \(i\) ,说明找到了偶环,无视
-
若当前点已被标记为 \(o\) ,说明找到了花!对其扩展出的交错路求出 \(lca\) ,则 \(lca\) 为花根。然后沿着花边走到花根,更新经过的点的 \(father\) 和标记,且将 \(pre\) 改为双向指针。
-
将该过程中找到的 \(o\) 型点加入队列。
//luogu P6113
//uoj 79
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long res=0,p=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')p=-1; ch=getchar();}
while(isdigit(ch)) res=res*10+ch-'0',ch=getchar();
return res*p;
}
const int maxn=1010,maxm=5e5+10;
int n,m;
int fa[maxn],type[maxn],pre[maxn],match[maxn],sccn,scc[maxn];
int head[maxn],cnt;
int ans;
/*
type -1 -> vis 0
type 0 -> vis 1 && point o
type 1 -> vis 1 && point i
*/
queue<int> q;
struct note {int nxt,to;}ed[maxm<<1];
void add(int u,int v) {ed[++cnt].nxt=head[u],ed[cnt].to=v,head[u]=cnt;}
inline void clear()
{
queue<int> empty;
swap(q,empty);
}
inline void init()
{
for(int i=1;i<=n;++i) fa[i]=i;
memset(type,-1,sizeof(type)),clear();
}
int getLca(int x,int y)
{
++sccn,x=fa[x],y=fa[y];
while(scc[x]!=sccn)
{
if(x) scc[x]=sccn,x=fa[pre[match[x]]];
swap(x,y);
}
return x;
}
void bloom(int x,int y,int lca)
{
while(fa[x]!=lca)
{
pre[x]=y,y=match[x];
if(type[y]==1) type[y]=0,q.push(y);
fa[x]=fa[y]=fa[lca],x=pre[y];
}
}
bool augment(int s)
{
init(),type[s]=0,q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=ed[i].nxt)
{
int v=ed[i].to;
// cout<<type[v]<<endl;
switch (type[v])
{
case -1:
pre[v]=u,type[v]=1;
if(!match[v])
{
for(int nxt=v,lst=u;nxt;lst=pre[nxt]) match[nxt]=lst,swap(match[lst],nxt);
return 1;
}
type[match[v]]=0,q.push(match[v]);
break;
case 1:
break;
case 0:
if(fa[u]==fa[v]) break;
int lca=getLca(u,v);
bloom(u,v,lca),bloom(v,u,lca);
}
}
}
return 0;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
for(int i=1;i<=n;++i) if(!match[i]) ans+=augment(i);
cout<<ans<<"\n";
for(int i=1;i<=n;++i) cout<<match[i]<<" ";
return 0;
}
标签:花树,ch,匹配,标记,int,增广,奇环 来源: https://www.cnblogs.com/Trinity31/p/15776717.html