Luogu P1144 最短路计数

题目描述

给出一个N个顶点M条边的无向无权图,顶点编号为1~N。问从顶点1开始,到其他每个点的最短路有几条。

输入输出格式

输入格式:

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

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

输出格式:

输出包括N行,每行一个非负整数,第i行输出从顶点1到顶点i有多少条不同的最短路,由于答案有可能会很大,你只需要输出mod 100003后的结果即可。如果无法到达顶点i则输出0。

输入输出样例

输入样例#1: 复制
5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5
输出样例#1: 复制
1
1
1
2
4

说明

1到5的最短路有4条,分别为2条1-2-4-5和2条1-3-4-5(由于4-5的边有2条)。

对于20%的数据,N ≤ 100;

对于60%的数据,N ≤ 1000;

对于100%的数据,N<=1000000,M<=2000000。


第一眼看到这个题,不难想到先求出起点到所有点的最短路然后bfs,但是再看一眼数据范围,显然是不可行的。。。
那么我们就从最短路那方面去考虑
SPFA就是bfs,而且在找到到一个点的最短路时会进行松弛操作
那么我们不难想到,在每次松弛一个点时,可能就找到了到这个点的最短路
然后不难想到,这时到这个点的最短路的条数等于到前驱节点的最短路的条数
但是到这个点的所有最短路不一定只经过一个相同的前驱,那么我们加一个条件,如果松弛的时候,到这个点的距离=到它的最短路的距离,那么最短路的条数加上当前前驱节点的最短路的条数,差不多就是个记忆化
如果一个节点被松弛了,那说明它之前的路径都不是最短路,直接赋值成前驱的最短路条数
然后还要mod 100003(我一开始居然就没注意到,然后就只有60分。。。现在审题太粗了。。。)

其实总结一下,好像一些算法的中间过程可以再进一步的修饰然后就可以解决一些相关问题,比如逆序对和这个,以后说不定还会碰到其他的,如果多的话就再开一篇单独列出


代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#define For(i,l,r) for(int i=l;i<=r;++i)
using namespace std;
int read()
{
    char c;
    int a=0;
    bool t=0;
    while((c=getchar())==' '||c=='\n'||c=='\r');
    if(c=='-')
    {
        t=1;
        c=getchar();
    }
    while(isdigit(c))
    {
        a*=10;
        a+=(c-'0');
        c=getchar();
    }
    return a*(t?-1:1);
}
struct line{
    int to,next;
}edge[4000001];
int n,m,ans[1000001],last[1000001],dis[1000001];
bool vis[1000001];
void add(int from,int to,int i)
{
    edge[i].next=last[from];
    last[from]=i;
    edge[i].to=to;
}
void spfa()
{
    int tx;
    memset(dis,127,sizeof dis);
    queue<int> q;
    q.push(1);dis[1]=0;vis[1]=1;ans[1]=1;
    while(!q.empty())
    {
        tx=last[q.front()];
        while(tx)
        {
            if(dis[edge[tx].to]>dis[q.front()]+1)
            {
                dis[edge[tx].to]=dis[q.front()]+1;
                ans[edge[tx].to]=ans[q.front()];
                if(!vis[edge[tx].to])
                {
                    q.push(edge[tx].to);
                    vis[edge[tx].to]=1;
                }
            }
            else if(dis[edge[tx].to]==dis[q.front()]+1)
            {
                ans[edge[tx].to]+=ans[q.front()];
                ans[edge[tx].to]%=100003;
            }
            tx=edge[tx].next;
        }
        vis[q.front()]=0;
        q.pop();
    }
}
int main()
{
    int tx,ty;
    n=read();
    m=read();
    m+=m;
    For(i,1,m)
    {
        tx=read();
        ty=read();
        add(tx,ty,i++);
        add(ty,tx,i);
    }
    spfa();
    For(i,1,n)
     printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值