1. 程式人生 > 實用技巧 >題解 AT1226 【電圧】

題解 AT1226 【電圧】

轉載註明來源: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;
}