模板 - 图论 - 基环树

基环树也可以直接套强连通缩点给秒了,但是事实上假如不需要缩点的话有更简单的写法。

下面是一种示例,必须是内向基环树,注意内向基环树的dfs上面有好几个时点:

0、进入环的时候,有时是从入度为0的点进入可能会有特殊操作,但是一般来说进入的时候主要是各个操作的初始化值。
1、当 color[u]!=0&&color[u]==c 时,第一次找到环的入口,可以这时候处理入口的值,但一般只交给3去做就好了。
2、当 color[u]!=0&&color[u]!=c 时,意味着找到其他dfs找过的点,有可能是环的其中一个入口,也有可能是树分叉,因为不是从入度为0的点开始找的甚至可能本身是一条链,这个时候处理不在环中的操作(op1)。
3、当 incirle 时,意味着在环中,处理在环中的操作(op2)。当 u==incirle 时,从环的入口退出,把在环中的标记清空,并处理退出环的操作(op3)。
4、以上都不是,意味着不在环中,处理不在环中的操作(op1)。

总之大概是这个样子,基环树也并不是都要存入度的,很多情况是可以直接用op1来继承,而且op1是躲不开的,因为有时候树会分叉。

const int MAXN = 2e5;

int n, G[MAXN + 5];
int color[MAXN + 5], cntcolor;

int incircle;
void dfs(int u, int c) {
    if(color[u]){
        if(color[u] == c){
            incircle = u;
            return;
        }
        //op1
        return;
    }
    color[u] = c;
    dfs(G[u], c);
    if(incircle) {
        //op2
        if(u == incircle) {
            //op3
            incircle = 0;
        }
    }
    //op1
}

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &G[i]);
    cntcolor = 0;
    for(int i = 1; i <= n; ++i) {
        if(!color[i]) {
            ++cntcolor;
            //op0
            dfs(i, cntcolor);
        }
    }
}

但是还是希望使用缩点法,缩点法没有这么多时点,全部都是建新图。

猜你喜欢

转载自www.cnblogs.com/KisekiPurin2019/p/11980305.html