点双联通

模板

#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <algorithm>
#define N 2000    //注意缩点后新建的图的顶点数可能达到2*n-1
#define M 600000  //缩点新建的图一定是树
using namespace std;

struct Edge{
    int u,v,id,next;
}edge[M*2];

int head[N],head2[N],cnt;
int dfn[N],low[N],color[N],depth,c,b;   //color数组指示每个割点或者点双连通分支的在新图的标号,割点标号均>=c
int cut[N];  //cut不为0表示是割点,分成cut+1块
stack<int>ss;
vector<int>dpt[N];          //每个点连通分支所包含的节点
vector<int>node_bcc[N];     //每个节点所属的点连通分支
int edge_belong[N];         //每条边所属的点连通分支

void addedge(int u,int v,int id,int * head){
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].id=id;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].id=id;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

void init(){
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    memset(dfn,0,sizeof(dfn));
    memset(color,0,sizeof(color));
    memset(cut,0,sizeof(cut));
    depth=c=cnt=0;
    for(int i=0;i<N;i++) dpt[i].clear(),node_bcc[i].clear();
    memset(edge_belong,0,sizeof(edge_belong));
}

void tarjan(int u,int fa){
    int i,j;
    dfn[u]=low[u]=++depth;
    for(i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa)continue;
        if(dfn[v]==0){
            ss.push(i);
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){
                cut[u]++;
                c++;
                do{
                    j=ss.top();
                    //printf("%d-%d ",edge[j].u,edge[j].v);
                    ss.pop();
                    edge_belong[edge[j].id]=c;
                    if(color[edge[j].u]!=c){
                        color[edge[j].u]=c;
                        dpt[c].push_back(edge[j].u);
                        node_bcc[edge[j].u].push_back(c);
                    }
                    if(color[edge[j].v]!=c){
                        color[edge[j].v]=c;
                        dpt[c].push_back(edge[j].v);
                        node_bcc[edge[j].v].push_back(c);
                    }
                }while(j!=i);
                //puts("");
            }
        }
        else{
            low[u]=min(low[u],dfn[v]);
            if(dfn[u]>dfn[v]) ss.push(i);   //这句话必须这么写!!否则打印点双联通内部的边会出错
        }
    }
}

int main(){
    int n,m,i,j,u,v;
    scanf("%d %d",&n,&m);
    init();
    for(i=1;i<=m;i++){
        scanf("%d %d",&u,&v);
        addedge(u,v,i,head);
    }
    for(i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i,0);
            cut[i]--;   //对于树根要减1
        }
    }
    /*printf("%d\n",c);

    for(i=1;i<=c;i++){
        for(j=0;j<dpt[i].size();j++) printf("%d ",dpt[i][j]);
        puts("");
    }*/

    b=c;
    for(i=1;i<=n;i++) if(cut[i]) color[i]=++c;

    for(i=1;i<=n;i++)
        for(j=0;j<dpt[i].size();j++){
            u=dpt[i][j];
            if(cut[u]){
                addedge(color[u],i,0,head2);
            }
        }

    /*for(i=1;i<=m;i++) printf("%d:%d\n",i,edge_belong[i]);
    for(i=1;i<=n;i++){
        for(j=0;j<node_bcc[i].size();j++) printf("%d ",node_bcc[i][j]);
        puts("");
    }*/
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值