题目链接
解题思路
采用Tarjan算法求割点
大致方法:
若一个节点是割点,则
1)若这个节点我们把它看作根节点(由于它的dfn在这个DFS树中最小,要加以特殊判断),则当且仅当它有
≥
2
\geq2
≥2个子树,它一定是割点。
2)若这个节点不是根节点,那么,当它的子树中有节点通过返祖边能够到达的最早节点(low)比这个节点的时间戳大于或等于,也就是说若这个节点的子树中有节点没法跑到这个节点的上边去,那么去掉这个节点,图自然不连通了
详细代码
tarjan部分
bool ct[MAXN];
int dfn[MAXN], tim;
int low[MAXN];
int ans;
void tarjan(int u, int rt) {
dfn[u] = low[u] = ++tim;
int ch = 0;
for(rg int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if(!dfn[v]) {//正常的通向儿子的边
tarjan(v, rt);
low[u] = Min(low[v], low[u]);
if(u != rt && low[v] >= dfn[u]) //过去的回不来了
ct[u] = 1;
ch++;
}
else low[u] = Min(low[u], dfn[v]);
}
if(u == rt && ch >= 2) ct[u] = 1;
}
全代码
//...IO etc.
const int MAXN = 20005;
const int MAXM = 100005;
struct Edge {
int v, nxt;
}e[MAXM << 1];
int n, m;
int head[MAXN], cnt;
void addedge(int u, int v) {
e[++cnt].v = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
bool ct[MAXN];
int dfn[MAXN], tim;
int low[MAXN];
int ans;
void tarjan(int u, int rt) {
dfn[u] = low[u] = ++tim;
int ch = 0;
for(rg int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if(!dfn[v]) {//正常的通向儿子的边
tarjan(v, rt);
low[u] = Min(low[v], low[u]);
if(u != rt && low[v] >= dfn[u]) //过去的回不来了
ct[u] = 1;
ch++;
}
else low[u] = Min(low[u], dfn[v]);
}
if(u == rt && ch >= 2) ct[u] = 1;
}
int main() {
//RS();
n = read(); m = read();
for(rg int i = 1; i <= m; i++) {
int u = read(), v = read();
addedge(u, v);
addedge(v, u);
}
for(rg int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i, i);
for(rg int i = 1; i <= n; i++)
if(ct[i]) ans++;
writeln(ans);
for(rg int i = 1; i <= n; i++)
if(ct[i]) writesp(i);
return 0;
}