fl0ppy-

导航

CF977E Cyclic Components 解题报告

CF977E Cyclic Components 解题报告

题意

第一感觉是并查集,但是不会。

你说得对,但是Tarjan大法好。

考虑暴力Tarjan缩点,满足了联通快这一性质,那环怎么办呢?

题中讲根据定义,一个环至少需要包含三个点,且边数与点数应当是相同的,也就是说对于每个联通快,只要它想变成环,那么它的大小起码是三,且对于这个联通快内的任意一个点,它的度数只能为二,因为度数为二是简单环的必要条件,在连通块前提下也是充分条件。

正常缩点即可,在缩的时候判断一下这个点是不是度数为二,再用一个sz数组记录一下其大小即可。

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N = 2e5 + 10;
inline int read(){
	int x = 0; bool f = 1; char c = getchar();
	for(; !isdigit(c); c = getchar()) if(c == '-') f = 0;
	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	return f ? x : -x;
}
int dnf[N], low[N], stk[N], tot, now, cnt, dcc[N], sz[N];
int ind[N];
int m;
int n;
bool instk[N], vis[N];
vector<int> e[N];
inline void tarjan(int x){
	dnf[x] = low[x] = ++tot;
	instk[x] = true;
	stk[++now] = x;
	for(const auto y : e[x]){
		if(!dnf[y]){
			tarjan(y);
			low[x] = min(low[y], low[x]);
		}
		else if(instk[y]){
			low[x] = min(dnf[y], low[x]);
		}
	}
	if(!(low[x] ^ dnf[x])){
		int tmp;
		cnt++;
		do{
			tmp = stk[now--];
			instk[tmp] = false;
			sz[cnt]++;//统计联通快大小
			dcc[tmp] = cnt;
			if(ind[tmp] != 2){
				vis[cnt] = false;
			}//如果联通块内有一个点入度不是二,直接标记跳过这个块,因为他不是环
		} while(tmp != x);
	}
}
main(){
	n = read(), m = read();
	for(int i = 1, aa, bb; i <= m; ++i){
		aa = read(), bb = read();
		e[aa].push_back(bb), e[bb].push_back(aa);
		ind[aa]++, ind[bb]++;
	}
	memset(vis, true, sizeof vis);
	for(int i = 1; i <= n; ++i){
		if(!dnf[i]){
			tarjan(i);
		}
	}
	int ans = 0;
	for(int i = 1; i <= cnt; ++i){
		if(vis[i] && sz[i] >= 3){//如果其内部点入度都为2且它的大小为三往上,那么满足条件
			ans++;
		}
	}
	cout << ans << endl;
}

Submission

posted on 2026-09-26 14:01  fl0ppy-  阅读(6)  评论(0)    收藏  举报