【Luogu7816】「Stoi2032」以父之名

「Stoi2032」以父之名

题目大意

给一个边权为 \(1\)\(2\) 的无向图,要求对每条边定向使得每个点的入度与出度之差的绝对值为 \(1\)。保证每个点的度数为奇数且存在一种方案。

Solution

考虑这个图只有 \(1\) 的边怎么做。因为每个点入度与出度之差为 \(1\),所以每个点的度数必为奇数,于是考虑建立一个虚点,向每个点连一条边,此时每个点度数为偶数(因为每个点的差均为 \(1\),而差值的和必为 \(0\),所以肯定是偶数个点),然后跑一次欧拉回路,欧拉回路所定方向即为一种可行解。

那么,加入 \(2\) 的边的时候呢?

我们分别考虑度数为奇数的点和度数为偶数的点。显然,对于入度为奇数的点,我们同样建立虚点向它们连边,又因为一条边会构造出两个度数为奇数的点,所以虚点的度数也为偶数,这个图仍是一个欧拉图。因为每个点的度数为奇数,所以原本度数为奇数的点必然有偶数条 \(2\) 和奇数条 \(1\),相对应的,原本度数为偶数的点必然有奇数条 \(2\) 和奇数条 \(1\)

我们再考虑一个神奇的贪心算法:设之前转移过来的时候边权是 \(w\),则我们优先选择边权为 \(w\) 的边,否则再去选另一种边权的边。这样,度数为奇数的点全部奇数边和偶数边会分别进行匹配,去掉新加的边,入度与出度差即为 \(1\);度数为偶数的点,只有一组偶数和奇数边匹配,其它仍然是分别匹配。

由此,我们在 \(O(V+E)\) 的时间内,解决了这道题。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int num=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while('0'<=ch&&ch<='9')num=num*10+ch-'0',ch=getchar();
	return num;
}
const int MAXN=1e6+10;
const int MAXM=8e6+10;
struct qwq{
	int v;
	int w;
	int nxt;
}edge[MAXM];
int cnt=-1;
int head[MAXN];
int h[MAXN][3];
int nxt[MAXM];
void adde(int u,int v,int w){
	edge[++cnt].nxt=head[u];
	edge[cnt].v=v;
	edge[cnt].w=w;
	nxt[cnt]=h[u][w];
	h[u][w]=head[u]=cnt;
}
int n,m;
int deg[MAXN];
int ans[MAXM];
void dfs(int u,int pre){
	while(~h[u][pre]&&ans[h[u][pre]]!=-1)h[u][pre]=nxt[h[u][pre]];
	if(!(~h[u][pre])){
		pre=((pre-1)^1)+1;
		while(~h[u][pre]&&ans[h[u][pre]]!=-1)h[u][pre]=nxt[h[u][pre]];
	}
	if(!(~h[u][pre]))return;
	ans[h[u][pre]]=0,ans[h[u][pre]^1]=1;
	int v=edge[h[u][pre]].v;
	h[u][pre]=nxt[h[u][pre]];
	dfs(v,pre);
	for(int i=head[u];~i;i=edge[i].nxt){
		head[u]=edge[i].nxt;
		if(ans[i]!=-1)continue;
		int v=edge[i].v;
		ans[i]=0,ans[i^1]=1;
		dfs(v,edge[i].w);
	}
}
int main(){
	memset(h,-1,sizeof(h));
	memset(nxt,-1,sizeof(nxt));
	memset(ans,-1,sizeof(ans));
	memset(head,-1,sizeof(head));
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read(),w=read();
		adde(u,v,w),adde(v,u,w);
		deg[u]++,deg[v]++;
	}
	for(int i=1;i<=n;++i){
		if(deg[i]&1){
			adde(n+1,i,1);
			adde(i,n+1,1);
		}
	}
	dfs(1,1);
	for(int i=1;i<=m;++i){
		putchar(ans[(i-1)<<1]?'1':'0');
	}
	putchar('\n');
}
posted @ 2021-08-23 22:33  FakeDragon  阅读(58)  评论(0编辑  收藏  举报