【BZOJ5056】OI游戏
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
2
01
10
Input2
4
0123
1012
2101
3210
Sample Output
Output1
1
Output2
6
1
Output2
6
题解:一开始看错题,以为是任意两点间的最短路都相同。。。
先求出1号点到所有点的最短路径图(如果1到所有点的最短路径都可能经过某条边,则这条边在最短路径图上)(注意这个图是有向的)(可以用SPFA或Dij,不过本人懒,用的Floyd)。然后我们只需要求出这个图的生成树个数了。由于要求的是外向树,所以用度数矩阵-邻接矩阵,再用辗转相除得到行列式的值即可。具体做法可以参见天赋那道题。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const ll P=1000000007; int n; char str[60]; int dis[60][60],map[60][60]; ll v[60][60],A,B; ll ans; int main() { scanf("%d",&n); int i,j,k; for(i=1;i<=n;i++) { scanf("%s",str+1); for(j=1;j<=n;j++) dis[i][j]=str[j]-'0',map[i][j]=dis[i][j]=(!dis[i][j]&&i!=j)?(0x3f3f3f3f):dis[i][j]; } for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(i!=j) { if(dis[1][i]+map[i][j]==dis[1][j]) v[j][j]++,v[i][j]--; } for(i=1;i<=n;i++) for(j=1;j<=n;j++) v[i][j]=(v[i][j]+P)%P; ans=1; for(i=2;i<=n;i++) { for(j=i;j<=n;j++) if(v[j][i]) break; if(j!=i) for(ans=P-ans,k=i;k<=n;k++) swap(v[i][k],v[j][k]); for(j=i+1;j<=n;j++) { A=v[i][i],B=v[j][i]; while(B) { ll tmp=A/B,temp=A; A=B,B=temp%B; for(ans=P-ans,k=i;k<=n;k++) v[i][k]=(v[i][k]-v[j][k]*tmp%P+P)%P,swap(v[i][k],v[j][k]); } } ans=ans*v[i][i]%P; } printf("%lld",ans); return 0; }