学这里的时候学了一些关于集合的知识,按照我对知识分类的习惯这里一并写上来
关系
等价关系
等价类
这个等价类就是一个子集族
{跟A等价的一路(包括A)},{跟B等价的一路(包括B)},…
集合的分类
全体等价类组成的集合就是一个分类嘛,这就是为什么说一个等价关系确定一个分类了
一个等价关系确定一个集合的分类
DFS生成树中边的分类
DFS树的性质
d[u]是访问u的时间截f[u]是弹出u的时间截
圈或环的定义
如果你要说顶点u在环内,那么你得找到一条从u出发经过一系列不重复的边回到u,如此你就可以说u在环或圈内
前面介绍那么多定义是为了什么?
为了看懂Pdf版蓝书(算法竞赛入门经典训练指南Pdf版)上这么一段话
tarjan思想
大概回顾一下tarjan的思想吧:随便从一个点出发dfs,根据访问顺序会产生一颗dfs树对吧(后面讨论的子节点,祖先节点都指dfs生成树里面的),假设当前点是u,下一个要访问的点是v,如果v及其v的子节点在不通过u的情况下找不到(意思就是不能访问到)u的父亲或祖先节点,那么把u删去,v又不能在失去u的情况找到u的父亲或祖先,那么图的连通分量是一定增加了的。
根据这个原理再搭配上时间截就可以实现tarjan算法啦
这里定义一个概念,方便后面的描述
def 能找祖时间截:对于一个节点v,它和它的子节点(能够找到的)(最小的)(非v的父亲)的祖先节点的时间截,记为low[u]。
void dfs(int u,int fa) {
dfn[u] = low[u] = ++index;
int child = 0;
for (int e = first0[u]; e!= -1; e=next0[e]){
if (!dfn[v[e]]) {
child++;
dfs(v[e], u);
low[u] = min(low[u], low[v[e]]);//如果你是第一次看这个代码,请忽略掉这句话
//如果你是第二次看:u的能找祖时间截等于u及其u子节点的能找祖时间截;
if (u!=root && low[v[e]] >= dfn[u]) {
flag[u] = 1;
//sum++;这里万万不可以!!!!!
}
}else if (dfn[v[e]]<dfn[u] &&v[e] != fa) { low[u] = min(low[u], dfn[v[e]]); }
//重点看这里,访问意味着这是一个祖先节点,这个时候记录下他的时间截
}
if (u == root && child >= 2) {
flag[u] = 1;
//sum++;这里万万不可以!!!!!
}
}
tarjan求割顶中的统计问题
本来每个节点访问且访问一次的,为什么在dfs里用sum统计会有问题呢?
只有一种可能,那就是sum会重复统计,可,这是为什么呢
这是因为u可以有v1,v2,v3,…,vn,多个儿子,如果v1已经不能在不通过u的情况下回到v1的非父祖先节点,而v2的情况和v1一样,那就会照成sum重复统计,所以这里要注意一下