P6413 [COCI2008-2009#3] NAJKRACI 题解

· · 题解

题目大意

题面已经很简洁了,这里不再赘述。

思路分析

定理1:对于一条最短路 u -> v,它的任意一个子路径 u_1 -> v_1 都是一条最短路。

证明:假设 u_1 -> v_1 不是最短路,那么一定可以找到一条路径 u_2 -> v_2 使得 u -> v 更短,与 u -> v 是最短路矛盾。

根据定理1,一张图 G,在固定源点 S 时,可以得到一张子图 G_1 使得 G_1 上任意一条边都在 S 到至少一个点的最短路径上,且不再 G_1 上的边都不在 S 到任意一个点的最短路上。我们称图 G_1 为最短路图。

判断一条边是否在最短路图上,只需判断 dis[u]+val[v] == dis[v] 是否成立。

求最短路图可以用 SPFA 来解决。

```cpp void spfa(int s){ memset(dis,0x3f,sizeof(dis)); memset(vis,false,sizeof(vis)); memset(book,false,sizeof(book)); dis[s]=0; vis[s]=true; q.push(s); while(!q.empty()){ int now=q.front(); vis[now]=false; q.pop(); for(int i=head[now];i;i=edge[i].next){ int v=edge[i].to; if(dis[v]>dis[now]+edge[i].val){ dis[v]=dis[now]+edge[i].val; if(!vis[v]){ vis[v]=true; q.push(v); } } } } for(int i=1;i<=m;i++){ if(dis[edge[i].from]+edge[i].val==dis[edge[i].to]) book[i]=true; } } ``` 定理2:任意的最短路图,一定不存在环。 证明:假设图中存在环 $u_1$ -> $u_2$ -> $u_3$ -> …… -> $u_n$ -> $u_1$,则有 $dis[u_1]+val[u_2] == dis[u_2]$,$dis[u_2]+val[u_3] == dis[u_3]$, …… $dis[u_n]+val[u_1] == dis[u_1]$。 因为边权都为正数,则有 $dis[u_n]>dis[u_1]$,$dis[u_1]>dis[u_n]$。 矛盾。 求出最短路图后,考虑如何统计答案。 设 `cnt1[i]` 表示 $S$ 到 $i$ 点的最短路数量,`cnt2[i]` 表示 $i$ 点到终点的最短路数量。 那么对于一条边 $e(u,v)$,根据乘法原理,答案为 $cnt1[u]*cnt2[v]$。 但题目中源点和终点并没有给出,只枚举源点,并不能知道终点。根据定理2可以考虑对最短路图拓扑排序,然后按照拓扑序倒序求 $cnt2$。 具体来说,枚举顺序为拓扑序倒序,当前点为 $u$,$u$ 点指向 $v_1$,$v_2$,则有 $cnt2[u]=cnt2[v_1]+cnt2[v_2]$。 $topo$ 数组与 $tag$ 记录拓扑序。 ```cpp void TopoSort(int s){ memset(ind,0,sizeof(ind)); memset(cnt1,0,sizeof(cnt1)); memset(cnt2,0,sizeof(cnt2)); cnt1[s]=1; q.push(s); tag=0; for(int i=1;i<=m;i++){ if(book[i]) ind[edge[i].to]++; } while(!q.empty()){ int now=q.front(); q.pop(); topo[++tag]=now; for(int i=head[now];i;i=edge[i].next){ if(!book[i]) continue; int v=edge[i].to; ind[v]--; if(ind[v]==0) q.push(v); cnt1[v]+=cnt1[now]; cnt1[v]%=mod; } } for(int i=tag;i;i--){ int now=topo[i]; cnt2[now]++; for(int j=head[now];j;j=edge[j].next){ if(!book[j]) continue; int v=edge[j].to; cnt2[now]+=cnt2[v]; cnt2[now]%=mod; } } } ```