[边双连通分量][LCA] Codeforces 231E

题目大意

给一个 (n)个点,(m) 条边的点仙人掌(点仙人掌定义:在简单连通图中,每个点最多属于一个简单环),(q) 次询问从 (x)(y) 有多少条简单路径,模1000000007。((n,mleq 10^5))

题解

考虑在一个环上,从 (u)(v) 有两条不同的简单路径。那么假设从 (u)(v) 经过了 (cnt) 个环,那么简单路径数目即为 (2^{cnt})。因为环是一个边双连通分量,我们可以使用Tarjan算法对边双连通分量进行缩点,形成一棵树,对于多个点缩成一个点的点,我们把它标记成黑色,单点的标记成白色,那么我们可以求出 (u,v) 缩点后的LCA,从而知道 (u)(v) 的路径上有多少个黑色点,也就是有多少个环。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

struct Graph{
    struct edge{int Next,to;};
    edge G[200010];
    int head[100010];
    int cnt;

    Graph():cnt(2){}
    void clear(int node_num=0){
        cnt=2;
        if(node_num==0) memset(head,0,sizeof(head));
        else fill(head,head+node_num+5,0);
    }
    void add_edge(int u,int v){
        G[cnt].to=v;
        G[cnt].Next=head[u];
        head[u]=cnt++;
    }
};
Graph G,G2;
int N,M,Q;

int dfn[100010],low[100010],bcc[100010],stk[100010],bcc_size[100010];
int index,bcc_id,top;

void Tarjan(int u,int in_edge){
    dfn[u]=low[u]=++index;
    stk[++top]=u;
    for(int i=G.head[u];i;i=G.G[i].Next){
        int v=G.G[i].to;
        if(!dfn[v]){Tarjan(v,i);low[u]=min(low[u],low[v]);}
        else if(i!=(in_edge^1)) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        ++bcc_id;int x;
        do{x=stk[top--];bcc[x]=bcc_id;++bcc_size[bcc_id];
        }while(x!=u);
    }
    return;
}

inline void build_new_graph(){
    for(RG u=1;u<=N;++u){
        for(int i=G.head[u];i;i=G.G[i].Next){
            int v=G.G[i].to;
            if(bcc[u]!=bcc[v]) G2.add_edge(bcc[u],bcc[v]);
        }
    }
}

int Deep[100010],Anc[100010][18],Count[100010];

void DFS_Init(int u,int fa){
    if(bcc_size[u]!=1) Count[u]=1+Count[fa];
    else Count[u]=Count[fa];
    Anc[u][0]=fa;
    for(int i=1;i<18;++i)
        Anc[u][i]=Anc[Anc[u][i-1]][i-1];
    for(int i=G2.head[u];i;i=G2.G[i].Next){
        int v=G2.G[i].to;
        if(v==fa) continue;
        Deep[v]=Deep[u]+1;
        DFS_Init(v,u);
    }
    return;
}

int LCA(int u,int v){
    int Root=1;
    if(u==Root || v==Root) return Root;
    if(Deep[u]<Deep[v]) swap(u,v);
    for(RG i=17;i>=0;--i){
        if(Deep[Anc[u][i]]>=Deep[v])
            u=Anc[u][i];
    }
    if(u==v) return u;
    for(RG i=17;i>=0;--i){
        if(Anc[u][i]!=Anc[v][i]){
            u=Anc[u][i];	
            v=Anc[v][i];
        }
    }
    return Anc[u][0];
}

const LL MOD=1000000007LL;

LL ExPow(LL b,LL n){
    LL x=1;
    LL Power=b%MOD;
    while(n){
        if(n&1) x=x*Power%MOD;
        Power=Power*Power%MOD;
        n>>=1;
    }
    return x;
}

int main(){
    Read(N);Read(M);
    for(RG i=1;i<=M;++i){
        int u,v;
        Read(u);Read(v);
        G.add_edge(u,v);
        G.add_edge(v,u);
    }
    for(int i=1;i<=N;++i)
        if(!dfn[i]) Tarjan(i,0);
    build_new_graph();
    DFS_Init(1,0);
    Read(Q);
    while(Q--){
        int u,v;
        Read(u);Read(v);
        u=bcc[u];v=bcc[v];
        int c=LCA(u,v);
        LL Ans=0;
        if(bcc_size[c]==1) Ans=ExPow(2,Count[u]+Count[v]-Count[c]*2);
        else Ans=ExPow(2,Count[u]+Count[v]-Count[c]*2+1);
        printf("%lld
",Ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/13559477.html