链接:P1144
题目描述
给出一个NN个顶点MM条边的无向无权图,顶点编号为1-N1−N。问从顶点11开始,到其他每个点的最短路有几条。
输入格式
第一行包含22个正整数N,MN,M,为图的顶点数与边数。
接下来MM行,每行22个正整数x,yx,y,表示有一条顶点xx连向顶点yy的边,请注意可能有自环与重边。
输出格式
共NN行,每行一个非负整数,第ii行输出从顶点11到顶点ii有多少条不同的最短路,由于答案有可能会很大,你只需要输出 ans \bmod 100003ansmod100003后的结果即可。如果无法到达顶点ii则输出00。
输入输出样例
输入 #1
5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5
输出 #1
1
1
1
2
4
说明/提示
11到55的最短路有44条,分别为22条1-2-4-51−2−4−5和22条1-3-4-51−3−4−5(由于4-54−5的边有22条)。
对于20%20%的数据,N ≤ 100N≤100;
对于60%60%的数据,N ≤ 1000N≤1000;
对于100%100%的数据,N<=1000000,M<=2000000N<=1000000,M<=2000000。
我第一眼看见最短路计数其实是一头雾水
但是我们可以再想一想,只要起点到一个点的距离有几种不同走法但是所过路径权值和相等就说明到这个点的最短路种数不同
也就是说,要求A到B的最短路的方案数,也就是说求A到B有多少条路径是最短且总权值相等的
我们如何解决这个问题呢?
根据最短路算法的方式,他是根据前面点最短路的情况来决定后面点的情况的。
那么如果我们要求当前点的方案数,我们就需要分两种情况
第一种是到当前点的最短路需要更新:
那么就要根据松弛操作里的:
if (dis[a[k].to]>dis[v]+a[k].dis)
dis[a[k].to]=dis[v]+a[k].dis;
当前的方案数也就是中间点v的方案数
第二种就是当前的到B的最短路不需要更新,即出现
dis[a[k].to]=dis[v]+a[k].dis
由于出现了与之前中间点不同的最短路相同的情况,则到当前B点的方案数加上这个中间点的方案数
注意:第一种情况的改变是通过替换,第二种情况是累加!
其他的都是一样的,根据SPFA的模板套一下即可
感慨:这种根据前驱来决定当前的思想真的很实用也很重要啊!
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
struct CZP
{
int next,to,dis;
}a[5000001];
int b[1000001],h[1000001],dis[1000001],k,n,m,sx,top,que[100001],tot[1000001];
void cun(int from,int to,int dis)
{
a[++top].next=h[from];
a[top].to=to;
a[top].dis=dis;
h[from]=top;
} //邻接表存储
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
h[i]=-1;
tot[1]=1;
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d",&x,&y);
cun(x,y,1);
cun(y,x,1);
} //无向图存储
for (int i=1;i<=n;i++)
dis[i]=2147483647;
dis[1]=0;
b[1]=1;
int head=0,tail=1;
que[tail]=1;
do
{
head=head%100000+1; //循环队列存储
int v=que[head];
k=h[v];
b[v]=0;
while (k!=-1)
{
if (dis[a[k].to]>dis[v]+a[k].dis)
{
dis[a[k].to]=dis[v]+a[k].dis;
tot[a[k].to]=tot[v]; //出现第一种情况,即最短路出现更短的需要新更新的情况
if (!b[a[k].to])
{
b[a[k].to]=1;
tail=tail%100000+1;
que[tail]=a[k].to;
}
}
else
if (dis[a[k].to]==dis[v]+a[k].dis) //出现第二种情况,即为出现与到当前点最短路相同的但中间点选取不同的情况
{
tot[a[k].to]+=tot[v];
tot[a[k].to]%=100003; //注意题目要求的取模
}
k=a[k].next;
}
}while (head<tail);
for (int i=1;i<=n;i++)
printf("%d\n",tot[i]); //输出即可
return 0;
}