[边双连通分量][LCA] Codeforces 231E
题目大意
给一个 \(n\)个点,\(m\) 条边的点仙人掌(点仙人掌定义:在简单连通图中,每个点最多属于一个简单环),\(q\) 次询问从 \(x\) 到 \(y\) 有多少条简单路径,模1000000007。\((n,m\leq 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\n",Ans);
}
return 0;
}