Tarjan的各种操作

最近比赛时做一道仙人掌的题就因为tarjan打错而WA0了,非常伤心,突然发现自己脑海中的tarjan真是一坨shit(我无意冒犯tarjan大神),所以去找了图论专题的PPT复习了一波。

dfn,low的含义在这里就不解释了。

有向图的tarjan:

有向图的tarjan只有一种用途,就是缩强联通分量。

2-SAT问题中也可以判合法性。

在有向图tarjan中,会遇到三种边:
1.树边
2.返祖边
3.横插边

横插边要无视掉。

当dfn[x] == low[x],的时候,就把栈里x及x以上的退栈,缩成一个点。

代码:

void dg(int x) {
    bd[x] = 1; d[++ d[0]] = x;
    dfn[x] = low[x] = ++ td;
    for(int i = final[x]; i; i = next[i])  {
        int y = to[i];
        if(!dfn[y]) dg(y), low[x] = min(low[x], low[y]); else
        if(bd[y]) low[x] = min(low[x], dfn[y]);
    }
    if(low[x] == dfn[x]) {
        for(; d[d[0]] != x; d[0] --)
            ff[d[d[0]]] = x, bd[d[d[0]]] = 0;
        d[0] --; ff[x] = x; bd[x] = 0;
    }
}

无向图tarjan:

无向图没有横插边。

第一种:

缩边双连通分量。

无向图的边双联通分量其实类似于有向图的强联通分量。

所以这个做法也差不多,只是要标记一下走过来的边是哪条,不能直接走回去。

这个的话一开始把边集数组的计数变量tot赋值为1,就可以用^1来判断。

代码:

void tar(int x, int la) {
    d[++ d[0]] = x; low[x] = dfn[x] = ++ td;
    for(int i = final[x]; i; i = next[i]) if(i != (la ^ 1)){
        int y = to[i];
        if(!dfn[y]) tar(y, i), low[x] = min(low[x], low[y]); else
        low[x] = min(low[x], dfn[y]);
    }
    if(dfn[x] == low[x]) {
        tz ++;
        do to[d[d[0]]] = tz; while(d[d[0] --] != x);
    }
}
第二种:

这种的作用是把仙人掌图搞成缩环树。

仙人掌图标志:任意一条边最多存在与一个简单环中

大概是dfs一下,把环上点的父亲设为环顶(第一次进入环的位置),环顶也可以视作在一个环上,它的父亲设为那个环顶的父亲。

这样缩了以后,处理一些信息(例如最短路)一般只用在lca处特判一下从哪边绕过去。

还是先求出low和dfn。

在仙人掌图中,一个点x,y是x的一个子节点。

如果low[y]>=dfn[x],则说明y上不去,则y的子图就被x隔开了,可以视x为环顶,fa[y]=x.

不然的话y上得去,fa[y] = fa[x]。

找桥点的话则不同,x只要有一个子节点y满足low[y]>=dfn[x],x就是一个桥点。

代码:

void tar(int x, int la) {
    low[x] = dfn[x] = ++ td;
    for(int i = final[x]; i; i = next[i]) if(i != (la ^ 1)){
        int y = to[i];
        if(!dfn[y]) tar(y, i), low[x] = min(low[x], low[y]); else
        low[x]  = min(low[x], dfn[y]);
    }
}
void tq(int x) {
    bz[x] = 1;
    for(int i = final[x]; i; i = next[i]) {
        int y = to[i]; if(bz[y]) continue;
        if(low[y] >= dfn[x]) fa[y] = x; else fa[y] = fa[x];
        tq(y);
    }
    bz[x] = 0;
}
第三种:

这个用来缩点双连通分量。

我们知道割点会存在于多个点双连通分量,所以缩点的话要新开点,并且退栈不要退了当前的点。

用于建圆方树。

Code:

void tar(int x, int la) {
    dfn[x] = low[x] = ++ tt;
    z[++ z[0]] = x;
    for(int i = e.final[x]; i; i = e.next[i]) {
        int y = e.to[i];
        if(!dfn[y]) {
            tar(y, i);
            low[x] = min(low[x], low[y]);
            if(low[y] >= dfn[x]) {
                td ++;
                while(z[z[0]] != y) e2.link(z[z[0] --], td);
                e2.link(z[z[0] --], td); e2.link(x, td);
            }
        } else if(i != (la ^ 1)) low[x] = min(low[x], dfn[y]);
    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值