tarjan学习以及模板总结

tarjan

无向图

割点:删掉某点后的联通块增加
:删掉某边后的联通快增加
点双联通图:删除某点后依然联通的图(或者没有割点的图)
边双联通图:删除某边后依然联通的图(或者没有桥的图)
双联通分量:点或者边的极大双联通分量
缩点:将一个联通分量等价成一个点

有向图

强连通:在有向图中如果两点间至少存在一条路径,则称两个顶点强连通
强连通图:任意两点都强联通的图
强连通分量:极大强联通子图

模板

求割点:

const int N=10005, M=1000005;
struct E { int next, to; }e[M];
int ihead[N], cnt;
void add(int x, int y) {
    e[++cnt]=(E){ihead[x], y}; ihead[x]=cnt;
    e[++cnt]=(E){ihead[y], x}; ihead[y]=cnt;
}
int FF[N], LL[N], tot, iscut[N];
void tarjan(int x, int f) {
    FF[x]=LL[x]=++tot; int s=0;
    for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f) {
        int y=e[i].to;
        if(!FF[y]) {
            ++s;
            tarjan(y, x);
            LL[x]=min(LL[x], LL[y]);
            if(LL[y]>=FF[x]) iscut[x]=1;//**关键点**
        }
        else if(LL[x]>FF[y]) LL[x]=FF[y];
    }
    if(f==-1 && s==1) iscut[x]=0;//**关键点**
}
int n, m;
void clr() {
    memset(ihead, 0, sizeof(int)*(n+1));
    memset(FF, 0, sizeof(int)*(n+1));
    cnt=0; tot=0;
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        for(int i=0; i<m; ++i) { int x, y; scanf("%d%d", &x, &y); add(x, y); }
        for(int i=1; i<=n; ++i) if(!FF[i]) tarjan(i, -1);
        for(int i=1; i<=n; ++i) if(iscut[i]) printf("%d%c", i, " \n"[i==n]);
        clr();
    }
    return 0;
}

求桥

void tarjan(int x, int f) {
    FF[x]=LL[x]=++tot; int s=0;
    for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f) {
        int y=e[i].to;
        if(!FF[y]) {
            ++s;
            tarjan(y, x);
            LL[x]=min(LL[x], LL[y]);
            if(LL[y]>FF[x]) iscutb[x]=1;//关键点
        }
        else if(LL[x]>FF[y]) LL[x]=FF[y];
    }
}

求强连通分量

void tarjan(int x) {
    FF[x]=LL[x]=++tot; s[++top]=x; vis[x]=1;
    for(int i=ihead[x]; i; i=e[i].next) {
        if(!FF[e[i].to]) tarjan(e[i].to), LL[x]=min(LL[x], LL[e[i].to]);
        else if(vis[e[i].to]) LL[x]=min(LL[x], FF[e[i].to]);
    }
    if(FF[x]==LL[x]) {
        int y;
        ++scc;
        do {
            y=s[top--];
            vis[y]=0;
            p[y]=scc;
        } while(x!=y);
    }
}
void tarjan() {
    for(int i=0; i<n; ++i) if(!FF[i]) tarjan(i);
}

求边双联通分量

void tarjan(int x, int last=0) {
    FF[x]=LL[x]=++tot; s[++top]=x; vis[x]=1;
    for(int i=ihead[x]; i; i=e[i].next) {
        if((i^1)!=last) {
            if(!FF[e[i].to]) tarjan(e[i].to), LL[x]=min(LL[x], LL[e[i].to]);
            else if(vis[e[i].to]) LL[x]=min(LL[x], FF[e[i].to]);
        }
    }
    if(FF[x]==LL[x]) {
        int y;
        ++scc;
        do {
            y=s[top--];
            vis[y]=0;
            p[y]=scc;
        } while(x!=y);
    }
}
void tarjan() {
    for(int i=0; i<n; ++i) if(!FF[i]) tarjan(i);
}

求点双连通分量

const int N=10005, M=1000005;
struct E { int next, to, fr; }e[M];
int ihead[N], cnt;
void add(int x, int y) {
    e[++cnt]=(E){ihead[x], y, x}; ihead[x]=cnt;
    e[++cnt]=(E){ihead[y], x, y}; ihead[y]=cnt;
}
int FF[N], LL[N], tot, vis[N], s[N], top, bcc;
vector<int> ans[N];
void tarjan(int x, int f) {
    FF[x]=LL[x]=++tot;
    for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f) {
        int y=e[i].to;
        if(!FF[y]) {
            s[++top]=i;
            tarjan(y, x);
            LL[x]=min(LL[x], LL[y]);
            if(LL[y]>=FF[x]) {
                ++bcc;
                ans[bcc].clear();
                int j;
                do {
                    j=s[top--];
                    if(!vis[e[j].fr]) ans[bcc].push_back(e[j].fr); vis[e[j].fr]=1;
                    if(!vis[e[j].to]) ans[bcc].push_back(e[j].to); vis[e[j].to]=1;
                } while(i!=j);
                for(vector<int>::iterator it=ans[bcc].begin(); it!=ans[bcc].end(); ++it)
                    vis[*it]=0;
            }
        }
        else if(LL[x]>FF[y]) LL[x]=FF[y];
    }
}
int n, m;
void clr() {
    memset(ihead, 0, sizeof(int)*(n+1));
    memset(FF, 0, sizeof(int)*(n+1));
    cnt=0; bcc=0; tot=0; top=0;
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        for(int i=0; i<m; ++i) { int x, y; scanf("%d%d", &x, &y); add(x, y); }
        for(int i=1; i<=n; ++i) if(!FF[i]) tarjan(i, -1);
        printf("%d\n", bcc);
        for(int i=1; i<=bcc; ++i) {
            sort(ans[i].begin(), ans[i].end());
            for(vector<int>::iterator it=ans[i].begin(); it!=ans[i].end(); ++it)
                printf("%d%c", *it, " \n"[it+1==ans[i].end()]);
        }
        clr();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35160381/article/details/80295179