BZOJ 5056 OI游戏 dijkstra(最短路径的数目)

来源:互联网 发布:淘宝代购手表靠谱吗 编辑:程序博客网 时间:2024/06/07 11:45

Description

小Van的CP最喜欢玩与OI有关的游戏啦~小Van为了讨好她,于是冥思苦想,终于创造了一个新游戏。
下面是小Van的OI游戏规则:
给定一个无向连通图,有N个节点,编号为0~N-1。图里的每一条边都有一个正整数权值,边权在1~9之间。
要求从图里删掉某些边(有可能0条),使得剩下的图满足以下两个条件:
1) 剩下的图是一棵树,有N-1条边。
2) 对于所有v (0 < v < N),0到v的最短路(也就是树中唯一路径长度)和原图中的最短路长度相同。
最终要报出有多少种不同的删法可以满足上述条件。(两种删法不同当且仅当存在两个点,
一种删法删完之后这两个点之间存在边而另外一种删法不存在。)
由于答案有可能非常大,良心的小Van只需要答案膜1,000,000,007的结果。
后记: 然而这游戏太高难度了,小Van的CP做不出来因此很不开心!
她认为小Van在故意刁难她,于是她与小Van分手了。。。
不过对于精通OI的你来说,这不过是小菜一碟啦!

Input

第一行一个整数N,代表原图结点。
接下来N行,每行N个字符,描绘了一个邻接矩阵。邻接矩阵中,
如果某一个元素为0,代表这两个点之间不存在边,
并且保证第i行第i列的元素为0,第i行第j列的元素(i≠j)等于第j行第i列的元素。
2≤N≤50

Output

一行一个整数,代表删法总方案数膜1,000,000,007的结果。

Sample Input

Input1
2
01
10


Input2
4
0123
1012
2101
3210

Sample Output

Output1
1
Output2
6

HINT








传送门
由于边权都是正的,我们知道不可能出现x--y--z和x--z--y在最短路径集中同时出现的情况。
题目要求求0~所有点的最短路径树的数目,
就可以直接算出0~所有点各自的最短路的数目,
然后乘起来就是答案。

怎么求最短路径的数目呢……
首先求一遍最短路,dis[x]表示0~x的最短路径长度,
然后假如对于原图的一条边(u,v),长度为x,而且满足:
dis[u]+x=dis[v]
那么x就可以是最短路径上的边,
对于同一个v,枚举所有的u,判断是否相等,满足的数目就是0~v的最短路径数目了



#include<bits/stdc++.h>using namespace std;const int N=55,Mod=1000000007;int n,dis[N],ANS[N];int Map[N][N];bool vis[N];void dijkstra(){memset(vis,0,sizeof(vis));memset(dis,100,sizeof(dis));dis[1]=0;for (int i=1;i<=n;i++){int Min=10000,mi=0;for (int j=1;j<=n;j++)if (!vis[j] && Min>dis[j]) Min=dis[j],mi=j;vis[mi]=1;for (int j=1;j<=n;j++)if (dis[j]>dis[mi]+Map[mi][j])dis[j]=dis[mi]+Map[mi][j];}}void solve(){for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (dis[i]+Map[i][j]==dis[j]) ANS[j]++;}int main(){scanf("%d",&n);char s[N];for (int i=1;i<=n;i++){scanf("%s",s);for (int j=0;j<n;j++) Map[i][j+1]=s[j]-'0';for (int j=1;j<=n;j++)if (!Map[i][j]) Map[i][j]=10000;}dijkstra();solve();int ans=1;for (int i=2;i<=n;i++)ans=((long long)ans*ANS[i])%Mod;printf("%d\n",ans);return 0;}

原创粉丝点击