转载注明来源:https://www.cnblogs.com/syc233/p/13647723.html


题意

给定\(n\) 个点 \(m\) 条边的无向图,现在要对每个点黑白染色。

若能够使一条边连接的两点颜色相同,其他边连接的两点颜色不同,则这条边合法。

求合法的边数。

\(2 \leq n \leq 10^5,1 \leq m \leq 2 \cdot 10^5\)

图可能不连通,不保证没有重边。


题解

删去边后图能够二染色,则这条边合法的必要条件是删边后的图是二分图,即没有奇环。那么合法的边必然在所有奇环上。

又因为偶环上的点一定是黑白交替出现,所以合法边必然不在偶环上。

不难想到对这个无向图求 DFS 树,再进行树上差分即可求出每一条树边在几个奇环和偶环中出现。

对于非树边(返祖边),分类讨论:

  • 若图中只有一个奇环,则这个奇环对应的非树边合法。

  • 若图中有不止一个奇环,再分类讨论:

    • 若有两个奇环相交,则这两个环会构成一个大环,并且这个大环一定是偶环,则两个奇环的返祖边均不合法。
    • 若有两个奇环不相交,则没有边同时处于这两个奇环,返祖边显然也不合法。

所以求出树边上合法边的总数,再判断奇环是否只有一个即可。


\(\text{Code}:\)

#include <cstdio>
#define maxn 100005
#define maxm 200005
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

struct edge
{
	int u,v,next;
	edge(int u,int v,int next):u(u),v(v),next(next){}
	edge(){}
}e[maxm<<1];

int head[maxn],k;

inline void add(int u,int v)
{
	e[k]=edge(u,v,head[u]);
	head[u]=k++;
}

int n,m;
int dep[maxn];
bool vis[maxm];
int odd[maxn],oddcnt;
int eve[maxn],evecnt;

inline void dfs(int u,int fa)
{
	vis[u]=true;
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if((i^1)==fa) continue;
		if(!vis[v])
		{
			dep[v]=dep[u]+1;
			dfs(v,i);
			eve[u]+=eve[v];
			odd[u]+=odd[v];
		}
		else if(dep[v]<dep[u])
		{
			if((dep[u]-dep[v])&1)
				++evecnt,++eve[u],--eve[v];
			else
				++oddcnt,++odd[u],--odd[v];
		}
	}
}

int main()
{
	// freopen("AT1226.in","r",stdin);
	read(n),read(m);
	for(int i=1;i<=n;++i) head[i]=-1;
	for(int i=1,u,v;i<=m;++i)
	{
		read(u),read(v);
		add(u,v);add(v,u);
	}
	for(int i=1;i<=n;++i)
		if(!vis[i]) dfs(i,-1);
	int ans=0;
	for(int i=1;i<=n;++i)
	{
		if(!dep[i]) continue;
		ans+=(!eve[i]&&odd[i]==oddcnt);
	}
	ans+=(oddcnt==1);
	printf("%d\n",ans);
	return 0;
}