最短路计数

链接

https://www.acwing.com/problem/content/description/1136/

题目

给出一个 N 个顶点 M 条边的无向无权图,顶点编号为 1 到 N。

问从顶点 1 开始,到其他每个点的最短路有几条。

输入格式
第一行包含 2 个正整数 N,M,为图的顶点数与边数。

接下来 M 行,每行两个正整数 x,y,表示有一条顶点 x 连向顶点 y 的边,请注意可能有自环与重边。

输出格式
输出 N 行,每行一个非负整数,第 i 行输出从顶点 1 到顶点 i 有多少条不同的最短路,由于答案有可能会很大,你只需要输出对 \(100003\) 取模后的结果即可。

如果无法到达顶点 i 则输出 0。

数据范围
\(1≤N≤10^5,\)
\(1≤M≤2×10^5\)
输入样例:

5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5

输出样例:

1
1
1
2
4

思路

先抛开这道题想一下最短路计数问题如何解决:
引入一个概念——“最短路树”:
假设每个点的父亲节点是这个点任意一条最短路径的前节点,那么就会形成一颗树,同时也会有其他条最短路的前节点,总之也会满足拓扑序。

求解最短路算法有:
bfs:只适用于边权为0和1的图,对于每个点更新最短路时可以直接更新最短路数量,因为每个点只会入队和出队一次,bfs的过程满足拓扑序。
Dijkstra:也在每个点更新最短路时可以直接更新最短路数量,因为每个点只会作为距离最近的节点出队一次。
spfa:不能直接计算最短路数量,因为每个节点都有可能多次入队和出队,spfa本质是对于以边相同的点为同一层优先搜索的,举个例子:

假设spfa先搜完1->2->3->4,3和4的最短路都确定,最短路条数都为1,再搜到1->6->7->8->3这条路时,只会将3的最短路计数加1,4的最短路还是1。
那么就会有人提出,当最短路数量更新时也加入队列,那么如果将3加入队列,对于4,不能确定最短路条数时加1还是加2。
但是这并不代表spfa不能求最短路条数。当边权有负时,只能用spfa计数,一种有效的做法是:先用spfa将每个点的单源最短路求出,枚举每条边,当有\(d[v[i]]=w[i]+d[u[i]]\)时,建一条边u到v的边,最后做拓扑DP,记录每个点可以被走到的方案数。(写这么多其实也是向讲一下spfa的做法,但是现在还没找到有这样的题,如果有机会给学弟们出题自己可以出一道)
对于这道题边权为1的图,可以直接用bfs,也可以用spfa(边权为1的spfa对于每个距离相同点可以保证同时搜到)。对于每个点v,u可以更新v的最短距离,那么v的计数等于u的计数;如果u的更新等于d[v],那么v的计数加上u的计数。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=400010,mod=100003;
int h[N],nex[M],e[M],idx;
int d[N],q[N],st[N],cnt[N],n,m;
void add(int u,int v){
    nex[idx]=h[u];
    e[idx]=v;
    h[u]=idx++;
}
void bfs(){
    memset(d,0x3f,sizeof d);
    d[1]=0;
    cnt[1]=1;
    int hh=0,tt=0;
    q[tt++]=1;
    st[1]=1;
    while(hh!=tt){
        int u=q[hh++];
        if(hh==N)  hh=0;
        st[u]=0;
        for(int i=h[u];~i;i=nex[i]){
            int v=e[i];
            if(d[v]>d[u]+1){
                d[v]=(d[u]+1)%mod;
                cnt[v]=cnt[u];
                if(!st[v]) {
                    q[tt++]=v;
                    if(tt==N) tt=1;
                    st[v]=1;
                }
            }
            else if(d[v]==d[u]+1){
                (cnt[v]+=cnt[u])%=mod;
                if(!st[v]) {
                    q[tt++]=v;
                    if(tt==N) tt=1;
                    st[v]=1;
                }
            }
        }
    }
}
int main(){
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    bfs();
    for(int i=1;i<=n;++i){
        cout<<cnt[i]<<endl;
    }
    return 0;
}
posted @ 2020-04-22 20:24  0x4f  阅读(248)  评论(1编辑  收藏  举报