给定一张 n n n 个顶点 m m m 条边的无向图,顶点的编号在 1 ∼ n 1 \sim n 1∼n 内,第 i i i 条无向边连接着顶点 x i x_i xi 与 y i y_i yi。
我们称顶点 v 0 , v 1 , ⋯ , v k − 1 v_0,v_1,\cdots, v_{k-1} v0,v1,⋯,vk−1 构成了一个大小为 k k k 的环,当且仅当 k ≥ 3 k \ge 3 k≥3,且对任意 0 ≤ i < k 0 \le i < k 0≤i<k,图中都存在一条连接顶点 v i v_i vi 与 v ( i + 1 ) mod k v_{(i+1) \operatorname{mod} k} v(i+1)modk 的无向边。我们称一个环 C C C 为最小环,当且仅当图中不存在一个大小严格小于 C C C 的环。
现在,你想要求出,图中有多少本质不同的最小环。
我们称两个环 C 1 ( u 0 , u 1 , ⋯ , u k − 1 ) C_1(u_0,u_1,\cdots, u_{k-1}) C1(u0,u1,⋯,uk−1) 与 C 2 ( v 0 , v 1 , ⋯ , v k − 1 ) C_2(v_0,v_1,\cdots, v_{k-1}) C2(v0,v1,⋯,vk−1) 不同,当且仅当组成这两个环的边不同。
在输入过程中对于一条边的两个点 x , y x,y x,y,从 x x x 做 B F S BFS BFS,记录每一个点到 x x x 的最短路程 d i s x dis_x disx。显然,最后 d i s y + 1 dis_y+1 disy+1 就是经过 x x x 的当前最小环长度。
在这个过程中同时求方案数。
令 f x = 1 f_x=1 fx=1;
当前遍历到点 u u u,先对它的所有邻接点 n x t nxt nxt 更新 d i s dis dis;(因为走的路径不是最短路,方案是无效的)
如果 d i s n x t = d i s u + 1 dis_{nxt}=dis_u+1 disnxt=disu+1,则 f n x t ← f n x t + f x f_{nxt}\gets f_{nxt}+f_x fnxt←fnxt+fx。(走了最短路径)
B F S BFS BFS 做完后, f y f_y fy 就是经过 x x x 的最小环个数,对每个 f y f_y fy 求和就是答案。
对于上面的一个过程,可以简单理解成:把图转换为一个 D A G DAG DAG(去掉了 d i s dis dis 值不连续的边),在上面做拓扑排序,求了方案数。
如果 d i s y + 1 < n u m dis_y+1
#include
using namespace std;
const int INF=0x3f3f3f3f;
int n,m,num=1e9,dis[3001],f[3001],ans;
int head[3001],nxt[12001],to[12001],cnt;
vector<int> v;
void add(int u,int v)
{
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void bfs(int st)
{
queue<int> q;
memset(dis,0x3f,sizeof(dis));
memset(f,0,sizeof(f));
dis[st]=0;
f[st]=1;
q.push(st);
while(q.size()){
int k=q.front();
q.pop();
for(int i=head[k];i;i=nxt[i]){
if(dis[to[i]]>dis[k]+1){
q.push(to[i]);
dis[to[i]]=dis[k]+1;
}
if(dis[to[i]]==dis[k]+1){
f[to[i]]+=f[k];
}
}
}
}
int main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
bfs(x);
if(dis[y]<num) num=dis[y],ans=0;
if(dis[y]==num) ans+=f[y];
add(x,y),add(y,x);
}
cout<<ans;
}