BZOJ 4894 有向图 外向生成树个数
4894: 天赋
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 191 Solved: 150
[Submit][Status][Discuss]
Description
小明有许多潜在的天赋,他希望学习这些天赋来变得更强。正如许多游戏中一样,小明也有n种潜在的天赋,但有
一些天赋必须是要有前置天赋才能够学习得到的。也就是说,有一些天赋必须是要在学习了另一个天赋的条件下才
能学习的。比如,要想学会"开炮",必须先学会"开枪"。一项天赋可能有多个前置天赋,但只需习得其中一个就可
以学习这一项天赋。上帝不想为难小明,于是小明天生就已经习得了1号天赋-----"打架"。于是小明想知道学习完
这n种天赋的方案数,答案对1,000,000,007取模。
Input
第一行一个整数n。
接下来是一个n*n的01矩阵,第i行第j列为1表示习得天赋j的一个前置天赋为i。
数据保证第一列和主对角线全为0。
n<=300
Output
第一行一个整数,问题所求的方案数。
Sample Input
8
01111111
00101001
01010111
01001111
01110101
01110011
01111100
01110110
01111111
00101001
01010111
01001111
01110101
01110011
01111100
01110110
Sample Output
72373
解析 本题其实就是求以1为起点 外向生成树的个数 还是用基尔霍夫满矩阵来写
定理题,证明过程比较难,记下结论吧
有向树:对于一个有向图,如果无视边的方向是一棵树,那么此有向图就称为有向树
外向树:有向树的特殊情况,下同,所有边的方向都是从根指向叶子
内向树:所有边的方向都是从叶子指向根
对于n个点的有向图,求出外向生成树个数:(其实就是这道题)
①先定义一个n*n的矩阵,a[i][i]初始化为i点的入度其它为0
②如果存在一条i到j的边,那么a[i][j]-1,最后删掉根的那一行和那一列
③求出对应(n-1)*(n-1)的行列式的值就是答案
对于有向图求内向生成树的个数只要将入度换成出度计算方式一样
1 #include <bits/stdc++.h> 2 #define pb push_back 3 #define mp make_pair 4 #define fi first 5 #define se second 6 #define all(a) (a).begin(), (a).end() 7 #define fillchar(a, x) memset(a, x, sizeof(a)) 8 #define huan printf("\n"); 9 #define debug(a,b) cout<<a<<" "<<b<<" "; 10 using namespace std; 11 typedef long long ll; 12 const int maxn=310,maxm=100,inf=0x3f3f3f3f; 13 const ll mod=1000000007; 14 ll a[maxn][maxn]; 15 ll det(int n) 16 { 17 ll ans = 1; 18 for (int i = 2; i <= n; i++) 19 { 20 for (int j = i + 1; j <= n; j++) 21 { 22 while (a[j][i] != 0) 23 { 24 ll u = a[i][i] / a[j][i]; 25 for (int k = i; k <= n; k++) 26 { 27 ll t = (a[i][k] - (ll)a[j][k] * u % mod + mod)% mod; 28 a[i][k] = a[j][k]; 29 a[j][k] = t; 30 } 31 ans = -ans; 32 } 33 } 34 ans = ans * a[i][i]% mod; 35 } 36 if (ans < 0) 37 { 38 //ans=-ans; 39 ans += mod; 40 } 41 return ans; 42 } 43 char s[500]; 44 int main() 45 { 46 int n; 47 scanf("%d",&n); 48 for(int i=1;i<=n;i++) 49 { 50 scanf("%s",s+1); 51 for(int j=1;j<=n;j++) 52 if(s[j]=='1') 53 a[j][i]--,a[j][j]++; 54 } 55 printf("%lld\n",det(n)); 56 }