ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

带花树

2022-01-07 20:33:37  阅读:179  来源: 互联网

标签:花树 ch 匹配 标记 int 增广 奇环


关于版权意识

参考抄袭_rqyBill_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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有