5056: OI游戏
Time Limit: 1 Sec Memory Limit: 64 MB
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
题意:
求最小路径树计数。
题解:
直接spfa,求出每个点的最短路径上的边数目乘起来就可以了。
感性理解。。。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
#define LL long long
#define mod 1000000007
#define N 55
using namespace std;
queue<int> q;
int n;
struct Edge{
int u, v, w, nxt;
}ed[3010];
int head[N], idc=0;
char ss[N];
int dis[N];
bool vis[N];
void adde(int u, int v, int w){
ed[++idc].u = u;
ed[idc].v = v;
ed[idc].w = w;
ed[idc].nxt = head[u];
head[u] = idc;
}
int main(){
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%s", ss+1);
for(int j=1; j<=n; j++)
if(ss[j] != '0') adde(i, j, ss[j]-'0');
}
memset(dis, 63, sizeof(dis));
dis[1] = 0; vis[1] = 1;
q.push( 1 );
while(!q.empty()){
int u = q.front(); q.pop();
vis[u] = 0;
for(int i=head[u]; i; i=ed[i].nxt){
int v = ed[i].v;
if(dis[v] > dis[u] + ed[i].w){
dis[v] = dis[u] + ed[i].w;
if( !vis[v] ) vis[v] = 1, q.push(v);
}
}
}
LL ans = 1;
for(int u=2; u<=n; u++){//root会被之后的统计到
LL tot = 0;
for(int i=head[u]; i; i=ed[i].nxt){
int v = ed[i].v;
if(dis[u] == dis[v] + ed[i].w) tot++;
}//取这tot个路径都可以保证u还有最短路径
(ans *= tot) %= mod;
}
printf("%lld", ans);
return 0;
}