小K与奇数

39 篇文章 0 订阅
8 篇文章 0 订阅
在无向图中寻找2n的匹配方案,条件是存在偶数长度路径。当边数为偶数且每个点度数为奇数时有解。通过构建DFS树,合并特定联通块的边,保持度数奇偶性,实现路径长度为偶数的匹配。采用线性时间复杂度算法解决此问题。
摘要由CSDN通过智能技术生成

n个点m条边的无向图,求出一个大小为 n 2 \frac n2 2n的匹配方案,两个点可以匹配当且仅当存在两个点在原图中的一条长度为偶数的路径,求一种方案使得所有匹配的路径不边相交但是可以点相交。

n , m &lt; = 1 e 5 n,m&lt;=1e5 n,m<=1e5

这个题。。。发现当且仅当边数为偶数并且每个点的度数都为奇数的时候有解。
如果我们不管路径长度为偶数这个条件,那么可以把 1 和 2 1和2 12, 3 和 4 3和4 34 2 i 和 2 i − 1 2i和2i-1 2i2i1连接起来那么如果 2 i 和 2 i − 1 2i和2i-1 2i2i1是一对匹配方案,那么这就是一个回路,如果不是,那么把他们连接起来相当于把两条路径合为一条,最后算下去一定还是一个回路,那么这就是一个欧拉回路问题,可以用经典算法做到线性。

那么考虑路径长度为偶数,发现只要每个点的度数都为奇数的那个条件只要满足,我们可以随便把原图魔改一下。怎么魔改比较好算呢?我们建一棵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 u2iu2i1v做这个过程,单出来的强制设为父亲那条边,让父亲去解决他,这样仍然满足每个点的度数都为奇数。

时间复杂度 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("");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值