2023NOIP A层联测9-紫罗兰

给定一张 n n n 个顶点 m m m 条边的无向图,顶点的编号在 1 ∼ n 1 \sim n 1n 内,第 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,,vk1 构成了一个大小为 k k k 的环,当且仅当 k ≥ 3 k \ge 3 k3,且对任意 0 ≤ i < k 0 \le i < k 0i<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,,uk1) C 2 ( v 0 , v 1 , ⋯   , v k − 1 ) C_2(v_0,v_1,\cdots, v_{k-1}) C2(v0,v1,,vk1) 不同,当且仅当组成这两个环的边不同。


在输入过程中对于一条边的两个点 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 fnxtfnxt+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+1disy+1<num,就是最小环的长度可以被更新,那么前面求的答案都无效了,将其清零。

#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;
}

你可能感兴趣的:(图论,算法)