转载注明来源: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;
}