n个点m条边的无向图,求出一个大小为 n 2 \frac n2 2n的匹配方案,两个点可以匹配当且仅当存在两个点在原图中的一条长度为偶数的路径,求一种方案使得所有匹配的路径不边相交但是可以点相交。
n , m < = 1 e 5 n,m<=1e5 n,m<=1e5
这个题。。。发现当且仅当边数为偶数并且每个点的度数都为奇数的时候有解。
如果我们不管路径长度为偶数这个条件,那么可以把
1
和
2
1和2
1和2,
3
和
4
3和4
3和4…
2
i
和
2
i
−
1
2i和2i-1
2i和2i−1连接起来那么如果
2
i
和
2
i
−
1
2i和2i-1
2i和2i−1是一对匹配方案,那么这就是一个回路,如果不是,那么把他们连接起来相当于把两条路径合为一条,最后算下去一定还是一个回路,那么这就是一个欧拉回路问题,可以用经典算法做到线性。
那么考虑路径长度为偶数,发现只要每个点的度数都为奇数的那个条件只要满足,我们可以随便把原图魔改一下。怎么魔改比较好算呢?我们建一棵dfs树,对于所有由三个点两条边连成的联通块,我们把两条边合并成一条,也就是直接把两端连起来,之前的边删掉,那么只走这种边,路径长度一定是偶数,又因为可以发现三个点的度数奇偶性都没变,那么新图就可以用上一段的做法求出不管路径长度为偶数的方案了。
但是对于所有的三个点两条边都做这个边数无法承受,考虑一个点 v v v和它的所有临界点为 u 1 , u 2 . . . . u_1,u_2.... u1,u2....,我们只对 u 2 i u 2 i − 1 和 v u_{2i}u_{2i-1}和v u2iu2i−1和v做这个过程,单出来的强制设为父亲那条边,让父亲去解决他,这样仍然满足每个点的度数都为奇数。
时间复杂度 O ( n ) O(n) O(n)
AC Code:
#include<bits/stdc++.h>
#define maxn 400005
using namespace std;
int n,m;
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=1;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
int in[maxn];
int ed[maxn][2],ned[maxn][2],id[maxn][2],cned;
void add(int &u,int v,int pt){
if(!u) u = v;
else{
ned[++cned][0] = ed[u][ed[u][0] == pt];
ned[cned][1] = ed[v][ed[v][0] == pt];
id[cned][0] = v , id[cned][1] = u;
u = 0;
}
}
bool vis[maxn],inq[maxn];
bool dfs(int now,int ff){
vis[now] = inq[now] = 1;
int curedge = 0;
for(int i=info[now];i;i=Prev[i])
if(!vis[to[i]]){
if(!dfs(to[i],i/2))
add(curedge,i/2,now);
}
else if(!inq[to[i]]) add(curedge,i/2,now);
inq[now] = 0;
if(!curedge) return 0;
if(ff) add(curedge,ff,now);
return 1;
}
vector<vector<int> >ans;
bool ban[maxn];
void dfs1(int now){
for(int i=info[now];i;info[now]=i=Prev[i])
if(!ban[i/2]){
ban[i/2] = 1;
dfs1(to[i]);
if(i/2 > cned){
if(ans.empty() || !ans.back().empty())
ans.push_back(vector<int>());
}
else
ans.back().push_back(id[i/2][i&1]),
ans.back().push_back(id[i/2][!(i&1)]);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
Node(u,v),Node(v,u);
ed[i][0] = u , ed[i][1] = v;
in[u]++,in[v]++;
}
bool flg = 0;
for(int i=1;i<=n;i++)
if(!(in[i]&1))
flg = 1;
if((m&1) || flg){ puts("0");return 0; }
for(int i=1;i<=n;i++)
if(!vis[i])
dfs(i,0);
memset(info,0,sizeof info);
cnt_e = 1;
for(int i=1;i<=cned;i++)
Node(ned[i][0],ned[i][1]),Node(ned[i][1],ned[i][0]);
for(int i=1;i<=n;i+=2)
Node(i,i+1),Node(i+1,i);
ans.push_back(vector<int>());
for(int i=1;i<=n;i++)
dfs1(i);
printf("%d\n",1);
for(int i=0;i<ans.size();i++){
if(ans[i].size()) printf("%d",ans[i].size());
for(int j=0;j<ans[i].size();j++)
printf(" %d",ans[i][j]);
puts("");
}
}