带花树模板代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3+5;
const int maxm = 4e5+5;
struct Edge{
int next,to;
}edge[maxm];
int head[maxn],tot=1;
int vis[maxn],pre[maxn],match[maxn],f[maxn],Id,id[maxn];
//vis[i]: 0(未染色) 1(黑色) 2(白色)
//match[i]: i的匹配点
//f[i]: i在带花树中的祖先
//pre[i]: i的非匹配边的另一点
//id: 找LCA用
int n,m,ans;
// queue<int> q;
void add(int u,int v){
edge[++tot].to=v;edge[tot].next=head[u];
head[u]=tot;
edge[++tot].to=u;edge[tot].next=head[v];
head[v]=tot;
}
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
int Lca(int x,int y){//查询x和y在带花树中的LCA
for(++Id;;swap(x,y)){//沿着增广路向上找lca
if(x){
x=find(x);//有可能环中有环(花中有花),所以用并查集找祖先,只处理祖先节点
if(id[x]==Id){//x,y在同一环中,一定会找到已被编号的点,该点即为LCA
return x;
}else{
id[x]=Id,x=pre[match[x]];//给点编号,并沿着非匹配边向上找
}
}
}
}
void blossom(int x,int y,int l,queue<int> &q){//缩点(开花),将x、y到LCA(l)路径中的点,缩为一点
while(find(x)!=l){
pre[x]=y,y=match[x];//增广路取反
if(vis[y]==2){//如果x、y的奇环中有白点,将其染为黑点,放入队列,让其去找不是环中的匹配点
vis[y]=1,q.push(y);
}
if(find(x)==x){//只改变是根的点
f[x]=l;
}
if(find(y)==y){
f[y]=l;
}
x=pre[y];//增广路取反
}
}
bool bfs(int s){//每次都以s为起点bfs,建带花树
for(int i=1;i<=n;i++){
vis[i]=pre[i]=0;f[i]=i;
}
queue<int> q;
// while(!q.empty()){q.pop();}
q.push(s);vis[s]=1;
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=head[now];i;i=edge[i].next){
int v=edge[i].to;
if((find(v)==find(now))||vis[v]==2)continue;//如果已经在同一个环(花)中或者是白点(意为这已经有匹配点),直接跳过,这种情况不会增加匹配数
if(!vis[v]){//如果没有被染色
vis[v]=2;pre[v]=now;//先染为白色,将前继点指向u
if(!match[v]){//如果没有被匹配过,直接匹配成功
for(int x=v,last;x;x=last){//增广路取反
last=match[pre[x]],match[x]=pre[x],match[pre[x]]=x;
}
return true;
}
vis[match[v]]=1;//如果被匹配过,则把匹配v的点染为黑色,放入队列中
q.push(match[v]);
}else{//v是黑色,形成奇环,则缩点(开花)。
int lca=Lca(now,v);
blossom(now,v,lca,q),blossom(v,now,lca,q);
}
}
}
return false;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++){
if(!match[i]&&bfs(i)){
ans++;
}
}
printf("%d\n",ans);
for(int i=1;i<=n;i++){
printf("%d%c",match[i],i==n?'\n':' ');
}
return 0;
}