template/ShinriiTin/undirected-tarjan

//Tarjan, 无向图求双连通分量和边双连通分量,O(n+m)
#define max_N (1000005)
#define max_M (2000005)
struct edge{
    int v,n;
}e[max_M<<1];
int head[max_N],tot=1;
//按顺序加边,编号为i的无向边对应的邻接表中的编号为2i和2i+1
inline void add_edge(int u,int v){
    e[++tot]={v,head[u]},head[u]=tot;
    e[++tot]={u,head[v]},head[v]=tot;
}
class Tarjan{
    private:
        int st[max_M<<1],top;
        std::stack<int>stack;
    public:
        int dfn[max_N],low[max_N],cnt;
        bool cut[max_N],bridge[max_M];
        //割点的bccno无意义,bcc从1开始编号
        int bccno[max_N],bcc_cnt;
        std::vector<int>bcc[max_N];
        //ebcc从1开始编号
        int ebccno[max_N],ebcc_cnt;
        std::vector<int>ebcc[max_N];
        inline void init(int n,int m){
            cnt=bcc_cnt=ebcc_cnt=0;
            for(int i=1;i<=n;++i)dfn[i]=cut[i]=bccno[i]=ebccno[i]=0;
            for(int i=1;i<=m;++i)bridge[i]=0;
        }
        void dfs(int x,int p){//第一次调用时p应该传入0
            low[x]=dfn[x]=++cnt;
            stack.push(x);
            int ch=0;
            for(int i=head[x],y;i;i=e[i].n){
                y=e[i].v;
                if(y==p)continue;
                if(!dfn[y]){
                    st[++top]=i;
                    ++ch,dfs(y,x);
                    low[x]=std::min(low[x],low[y]);
                    if(dfn[x]<=low[y]){
                        cut[x]=1;
                        bcc[++bcc_cnt].clear();
                        for(int j,u,v;;){
                            j=st[top--];
                            u=e[j^1].v,v=e[j].v;
                            if(bccno[u]!=bcc_cnt)bcc[bccno[u]=bcc_cnt].push_back(u);
                            if(bccno[v]!=bcc_cnt)bcc[bccno[v]=bcc_cnt].push_back(v);
                            if(j==i)break;
                        }
                    }
                    if(dfn[x]<low[y])bridge[i>>1]=1;
                    //视情况需要单独处理重边,重边不是桥
                }
                else if(dfn[y]<dfn[x]){
                    st[++top]=i;
                    low[x]=std::min(low[x],dfn[y]);
                }
            }
            if(!p&&ch==1)cut[x]=0;
            //如果dfs树中的根节点只有一个子树与其相连,它肯定不是割点
            if(dfn[x]==low[x]){
                ebcc[++ebcc_cnt].clear();
                ebccno[x]=ebcc_cnt;
                ebcc[ebcc_cnt].push_back(x);
                while(stack.top()!=x){
                    ebccno[stack.top()]=ebcc_cnt;
                    ebcc[ebcc_cnt].push_back(stack.top());
                    stack.pop();
                }
                stack.pop();
            }
        }
};