題解 AT1226 【電圧】
阿新 • • 發佈:2020-09-10
轉載註明來源: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; }