【双联通分量】 HDOJ 5215 Cycle

BC的官方题解已经讲的不能再清楚了。。。

对于问题1,我们只需要进行二分图染色判定这个图是否是二分图即可
二分图中必定不存在奇环,而非二分图中必定存在奇环
对于问题2,首先我们注意到一个环一定存在于双联通分量(既去掉任何一条边后仍然联通的点集)内
通过tarjan算法,可以分离出所有的双联通分量,然后分别检查其中是否存在偶环
对于一个双联通分量,如果它仅仅是一个环,那么只需判断它是否是偶环即可
否则其中必定存在两个缠绕(共享至少一条边)的环,若这两个环的都是奇环,那么去掉共享的边之后可以组成一个大偶环
因此,除非一个双联通分量仅仅是一个奇环,那么其中必定存在偶环
而我们可以发现,若一个非单点的双联通分量仅仅由一个环构成,那么它的点数必定等于边数,据此判断即可
时间复杂度O(N + M)


#include <iostream>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <cmath>
#include <time.h>
#define maxn 100005
#define maxm 600005
#define eps 1e-12
#define mod 998244353
#define INF 0x3f3f3f3f
#define PI (acos(-1.0))
#define lowbit(x) (x&(-x))
#define mp make_pair
#define ls o<<1
#define rs o<<1 | 1
#define lson o<<1, L, mid 
#define rson o<<1 | 1, mid+1, R
#pragma comment(linker, "/STACK:102400000,102400000")
#define pii pair<int, int>
typedef long long LL;
typedef unsigned long long ULL;
//typedef int LL;
using namespace std;
LL qpow(LL a, LL b){LL res=1,base=a;while(b){if(b%2)res=res*base;base=base*base;b/=2;}return res;}
LL powmod(LL a, LL b){LL res=1,base=a;while(b){if(b%2)res=res*base%mod;base=base*base%mod;b/=2;}return res;}
// head

struct Edge
{
	int u, v;
	Edge *next;
}*H[maxn], E[maxm], *edges;

int dfn[maxn];
int low[maxn];
int cnt[maxn];
int vis[maxn];
int tcnt[maxn];
int belong[maxn];
stack<int> s;
int n, m, dfs_clock, scnt, ok;

void addedges(int u, int v)
{
	edges->u = u;
	edges->v = v;
	edges->next = H[u];
	H[u] = edges++;
}

void init()
{
	scnt = dfs_clock = 0;
	edges = E;
	memset(H, 0, sizeof H);
	memset(dfn, 0, sizeof dfn);
	memset(cnt, 0, sizeof cnt);
	memset(vis, -1, sizeof vis);
	memset(tcnt, 0, sizeof tcnt);
}

void tarjan(int u, int fa)
{
	dfn[u] = low[u] = ++dfs_clock;
	s.push(u);
	bool flag = 1;
	for(Edge *e = H[u]; e; e = e->next) {
		int v = e->v;
		if(v == fa && flag) {
			flag = 0;
			continue;
		}
		if(!dfn[v]) {
			tarjan(v, u);
			low[u] = min(low[u], low[v]);
		}
		else low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u]) {
		++scnt;
		int t;
		do{
			t = s.top();
			s.pop();
			belong[t] = scnt;
			cnt[scnt]++;
		}while(t != u);
	}
}

void dfs(int u, int fa)
{
	for(Edge *e = H[u]; e; e = e->next) if(e->v != fa) {
		if(vis[e->v] == -1) vis[e->v] = vis[u] ^ 1, dfs(e->v, u);
		else if(vis[e->v] == vis[u]) ok = 1;
	}
}

void work()
{
	int u, v;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i++) {
		scanf("%d%d", &u, &v);
		addedges(u, v);
		addedges(v, u);
	}
	ok = 0;
	for(int i = 1; i <= n; i++) if(vis[i] == -1) vis[i] = 0, dfs(i, 0);
	if(ok) printf("YES\n");
	else printf("NO\n");

	for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i, 0);
	for(Edge *e = E; e != edges; e += 2)
		if(belong[e->v] == belong[e->u]) tcnt[belong[e->u]]++;
	ok = 0;
	for(int i = 1; i <= scnt; i++) {
		if(cnt[i] == tcnt[i] && cnt[i] % 2 == 0) ok = 1;
		else if(cnt[i] != tcnt[i] && cnt[i] != 1) ok = 1;
	}
	if(ok) printf("YES\n");
	else printf("NO\n");
}

int main()
{
	int _;
	scanf("%d", &_);
	while(_--) {
		init();
		work();
	}
	
	return 0;
}


你可能感兴趣的:(双联通分量)