Little Q is crazy about graph theory, and now he creates a game about graphs and trees.
There is a bi-directional graph with n nodes, labeled from 0 to n−1. Every edge has its length, which is a positive integer ranged from 1 to 9.
Now, Little Q wants to delete some edges (or delete nothing) in the graph to get a new graph, which satisfies the following requirements:
(1) The new graph is a tree with n−1 edges.
(2) For every vertice v(0
题意
对于给定的图(双向路),有多少种子图,使其满足下列条件:
- 子图为含 n-1 条边的树。
- 对于任意一点 v ,子图中 0 到 v 的距离等于原图 0 到 v 的最小距离
解题思路
首先对于原图处理出 0 到任意点的最小距离。
对于子树个数的判断,可以作如下思考:
对于到 0 的最小距离最小的 m 个点,剩下的 n-m 个点的距离必然大于等于 m 个点中的任意一点,即 n-m 个点中的任意一点 p 可以通过 m 个点中的一个或多个点 q 向其连边进行扩展。其中若
dis[0][q] + src[q][p] == dis[0][p]
,则表明 p 点可以通过与 q 点连边进行扩展,cnt[p]
表示 p 点有多少种向 m 点集合并的方式如此将此 m 个点的集合不断扩展,直到 n 个点都属于这个集合。
对于答案,即为每个点向点集中合并的方案数的积, ∏n−1i=0cnt[i]
代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int n, src[55][55], dis[55][55], cnt[55];
char s[55][55];
bool vis[55];
pair<int, int> p[55];
bool cmp(pair<int, int> a, pair<int, int> b) {
return a.second < b.second;
}
void floyd() {
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
int solve()
{
if(p[n-1].second >= mod) return 0;
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
vis[0] = 1;
for(int i=1, idx;i<n;i++)
{
idx = p[i].first;
for(int j=0;j<n;j++)
if(vis[j] && dis[0][j] + src[j][idx] == p[i].second)
cnt[idx]++;
vis[idx] = 1;
}
long long ans = 1;
for(int i=1;i<n;i++)
(ans *= cnt[i]) %= mod;
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf(" %s",&s[i]);
for(int j=0;j<n;j++) {
dis[i][j] = src[i][j] = s[i][j] - '0';
if(dis[i][j] == 0 && i!=j) dis[i][j] = src[i][j] = mod;
}
}
floyd();
p[0] = make_pair(0, 0);
for(int i=1;i<n;i++)
p[i].first = i, p[i].second = dis[0][i];
sort(p, p+n, cmp);
printf("%d\n", solve());
}
}