题目:
1.树的计数
【问题描述】
图和树有很密切的关系。某一天牙神产生了一个很奇怪的想法:删去一些边把一个无向图变成一个树,也就是将边留下 N − 1 条。而且对于任意一个点 i,要保证现在树中的边满足 1 号点到 i 号点的路径长度等于原图中 1 号点到 i 号点的最短路长度。牙神请你帮忙数一下有多少个目标树满足要求。答案对1000000007 取模。
【输入格式】
第一行,一个整数N。接下来 N 行每行 N 个数,第 i 行第 j 个数表示 i 和 j 的连边关系,0 表示没有边。
【输出格式】
一个整数,表示答案。
【输入样例】
3
0 2 1
2 0 1
1 1 0
【输出样例】
2
【数据规模】
对于 40%数据 N ≤ 50
对于 100%数据 N ≤ 1000,每条边边权在[0,14]内。
题解:按照最短路径的思想,记录每一个点被假如已选集合的时候,有几个点可以更新这个点的最短路径。
按照乘法原理,最后答案就是每一个点的有几个点可以更新这个点的最短路径相乘
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=1005,M=1000000007; int n,a[N][N],dis[N],g[N],f[N]; int main() { freopen("treecnt.in","r",stdin); freopen("treecnt.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++)scanf("%d",&a[i][j]); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (!a[i][j])a[i][j]=10000005; for (int i=1;i<=n;i++)dis[i]=a[1][i]; for (int i=1;i<=n;i++)g[i]=1; long long ans=1; f[1]=1; for (int i=1;i<n;i++) { int l=-1; for (int j=1;j<=n;j++) if (!f[j]&&(l==-1||dis[l]>dis[j]))l=j; f[l]=1; (ans*=g[l])%=M; for (int j=1;j<=n;j++) if (!f[j]) { if (dis[j]>dis[l]+a[l][j]) dis[j]=dis[l]+a[l][j],g[j]=0; if (dis[j]==dis[l]+a[l][j])g[j]++; } } printf("%lld",ans); }