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;
}
浙公网安备 33010602011771号