题目链接

P3388 【模板】割点(割顶)

解题思路

最近学的东西太杂了,多写点博客免得自己糊里糊涂的过去了。

这个题求割点,感觉这篇文章写得挺好。

割点是啥?如果去掉这个点之后连通图变成多个不连通图了,那这个点就是割点。

那我们如何求割点呢?显然,我们可以无视复杂度枚举一下每个点然后\(DFS\)一下看看图连不连通。

那我们能不能在更好的复杂度下求割点呢?可以。

首先,深搜一下这个连通图,标记一下深度(\(dep\),或者大部分博客里写的\(DFS\)序\(dfn\),但我觉得深度更易理解)。

void dfs(int p,int f){//p是当前节点,f是dfs树中p的父亲节点
int q,i;
dep[p]=dep[f]+1;
for(i=head[p];i;i=e[i].near){
q=e[i].end;
if(!dep[q])//这句话保证了进入dfs的点必然是没访问过的
dfs(q,p);
}
}

然后呢,我们观察一下这个\(dfs\)树的性质。

首先,这个树的根节点只要伸出去多个枝(\(\geq 1\)),那么它就是一个割点。

因为这两个(或多个)枝条之间只要有连接,那么必定在\(dfs\)树中只会以一个枝条的形式出现。

如下图,红色代表能够走的边,蓝色因为根节点已经访问过了而无法继续走。

于是我们有了这个\(DFS\)的代码:

void dfs(int p,int f){
int q,i,child=0;
dep[p]=dep[f]+1;
for(i=head[p];i;i=e[i].near){
q=e[i].end;
if(!dep[q]){
dfs(q,p);
if(!f)child++;
}
}
if(!f&&child>1)cut[p]=1,num++;//只要有多个孩子就是割点
}

别急,还有非根节点没有解决呢。

其实与根节点同理,非根节点如果有两个以上的互相不能联通的枝条的话,那它就是一个割点了。

那么怎么判断它的两个枝条连通与否呢?很简单,只要看它有没有枝条不能走到它祖先位置上即可。

也就是说,把一个点的子树看成\(Branch1\),把其祖先那一串东西看成\(Branch2\),如果这俩东西不能连通那么这个点就是割点啦。

于是引入新的数组\(low\),保存的内容是其能够到达的\(dep\)最小的\(dep\)值(即最浅深度)。

然后新鲜出炉的DFS长这样:

void dfs(int p,int f){
int q,i,child=0;
dep[p]=low[p]=dep[f]+1;//最小的深度先设为自己的深度
for(i=head[p];i;i=e[i].near){
q=e[i].end;
if(!dep[q]){
dfs(q,p);
if(low[q]<low[p])low[p]=low[q];//如果孩子能到更浅的地方那么更新该节点的最浅深度
if(low[q]>=dep[p]&&f&&!cut[p])cut[p]=1,num++;
if(!f)child++;
}
if(dep[q]<low[p])low[p]=dep[q];//重点在这一步
}
if(!f&&child>1)cut[p]=1,num++;
}

代码中注释出的重点,为什么不能写成\(low[p]=min(low[p],low[q])\)呢?

其实是因为,如果已经在某个节点遍历到一根返祖边,那么这个节点的\(low\)值由此会变成孩子节点的\(low\)即祖先节点的\(dep\)值,再对其其他孩子进行遍历的时候,会有可能将孩子节点的\(low\)值更新为该节点的\(low\)即祖先的\(dep\)值,而将本该是割点的点忽略掉。

如果不理解,试试\(debug\)一下这组数据:

6 7

1 2

2 3

3 4

2 4

2 5

5 6

1 6

AC代码

#include<stdio.h>
int num,head[20010],cnt,low[20010],dep[20010],cut[20010],n,m;
struct Edge{
int end,near;
}e[200002];
void add(int a,int b){
e[++cnt].end=b;e[cnt].near=head[a];head[a]=cnt;
}
void dfs(int p,int f){
int q,i,child=0;
dep[p]=low[p]=dep[f]+1;
for(i=head[p];i;i=e[i].near){
q=e[i].end;
if(!dep[q]){
dfs(q,p);
if(low[q]<low[p])low[p]=low[q];
if(low[q]>=dep[p]&&f&&!cut[p])cut[p]=1,num++;
if(!f)child++;
}
if(dep[q]<low[p])low[p]=dep[q];
}
if(!f&&child>1)cut[p]=1,num++;
}
int main(){
int i,u,v;
scanf("%d%d",&n,&m);
for(i=0;i<m;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(i=1;i<=n;i++)
if(!dep[i])dfs(i,0);//原图可能不连通
printf("%d\n",num);
for(i=1;i<=n;i++)if(cut[i])printf("%d ",i);
return 0;
}

P3388 【模板】割点(割顶) 题解 (Tarjan)的更多相关文章

  1. 洛谷 P3388 割点(割顶) 题解

    题面:     割点性质:     节点 u 如果是割点,当且仅当存在 u 的一个子树,子树中没有连向 u 的祖先的边(返祖边).     换句话说,如果对于一个点u,它的子节点是v,如果low[v] ...

  2. Tarjan求割点(割顶) 割边(桥)

    割点的定义: 感性理解,所谓割点就是在无向连通图中去掉这个点和所有和这个点有关的边之后,原先连通的块就会相互分离变成至少两个分离的连通块的点. 举个例子: 图中的4号点就是割点,因为去掉4号点和有关边 ...

  3. $割点割顶tarjan$

    原题 #include <bits/stdc++.h> using namespace std; typedef long long LL; inline LL read () { LL ...

  4. 洛谷P3388 【模板】割点(割顶)(tarjan求割点)

    题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式: 第一行输出割点个数 第二行按照 ...

  5. 【模板】割点(割顶) Tarjan

    题目背景 割点 题目描述 给出一个nnn个点,mmm条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,mn,mn,m 下面mmm行每行输入x,yx,yx,y表示xxx到yyy有一条边 ...

  6. Tarjan求割点 || Luogu P3388 【模板】割点(割顶)

    题面:P3388 [模板]割点(割顶) 题解:无 代码: #include<cstdio> #include<iostream> #include<cstring> ...

  7. 洛谷 P3388 【模板】割点(割顶)(Tarjan)

    题目链接 https://www.luogu.org/problemnew/show/P3388 模板题 解题思路 什么是割点? 怎样求割点? dfn :即时间戳,一张图的dfs序(dfs遍历时出现的 ...

  8. P3388 【模板】割点(割顶)

    P3388 [模板]割点(割顶) 题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式 ...

  9. P3388 【模板】割点(割顶)&& 桥

    题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式: 第一行输出割点个数 第二行按照 ...

随机推荐

  1. SOHO 程序员

    SOHO 程序员:从事程序开发.维护的家居办公人员. 一.自由程序员 SOHO程序员代表一种自由.弹性而新型的工作方式.SOHO,代表一种新经济.新概念. 是一些热爱软件开发的一族. SOHO程序员 ...

  2. Win10永久禁用驱动程序强制签名

    在win10下用一个命令就可以禁用驱动程序强制签名 1.禁止强制签名,以管理员的身份运行cmd 执行以下命令 bcdedit.exe /set nointegritychecks on 恢复默认验证, ...

  3. C++ part9

    1.静态多态和动态多态 静态多态:函数重载,模板.编译期间完成. 动态多态:虚函数.运行期间实现. 2.模板的实现和优缺点 函数模板的代码并不能直接编译成二进制代码,而是要实例出一个模板实例.写了模板 ...

  4. 7816协议时序和采用UART模拟7816时序与智能卡APDU指令协议

    7816时序 7816时一个比较早的老通讯时序了,最近项目上需要用UART模拟所以,简单学习时序. 时序比较简单,熟悉UART的一眼看着就像是串口的时序,只是他没有停止位,取而代之的就是保护时间gur ...

  5. 如何将多个 Apple 设备中保存在 iCloud 里面密码同步

    如何将多个 Apple 设备中保存在 iCloud 里面密码同步 iCloud 钥匙串 密码同步 数据迁移 iOS iCloud 钥匙串会记住一些信息,因此您就无需记忆这些信息. 它会在您批准的任何设 ...

  6. GitHub GraphQL API v4 & GitHub REST API v3

    GitHub, GraphQL API, v4 ,REST API, v3, GraphQL, https://developer.github.com/v4/ https://developer.g ...

  7. Raspberry Pi 电路图模拟器

    Raspberry Pi 电路图模拟器 Circuit Diagram / Circuit Graph https://fritzing.org/learning/tutorials/building ...

  8. wireshark 获取指定进程id的数据

    >netstat -aon | findstr 11380 TCP 191.127.1.7:57936 29.225.107.216:3734 ESTABLISHED 11380 过滤器: tc ...

  9. qt char与code的相互转换

    QString str = "A"; QChar c = str.at(0); // int v_latin = c.toLatin1(); // 不能转中文 int v_lati ...

  10. 详解支付体系颠覆者NGK公链:如何通过呼叫河马智能合约加速转账?

    纵观全球加密货币市场,至今为止,全球已经发行的加密货币以及数字代币的数量已经超过了7000种,且未来还将会有更多的加密货币或数字代币出现.在众多加密货币项目中,投资者很难在众多的项目里甄别项目的好坏以 ...