小明有许多潜在的天赋,他希望学习这些天赋来变得更强。正如许多游戏中一样,小明也有n种潜在的天赋,但有
一些天赋必须是要有前置天赋才能够学习得到的。也就是说,有一些天赋必须是要在学习了另一个天赋的条件下才
能学习的。比如,要想学会"开炮",必须先学会"开枪"。一项天赋可能有多个前置天赋,但只需习得其中一个就可
以学习这一项天赋。上帝不想为难小明,于是小明天生就已经习得了1号天赋-----"打架"。于是小明想知道学习完
这n种天赋的方案数,答案对1,000,000,007取模。
第一行一个整数n。
接下来是一个n*n的01矩阵,第i行第j列为1表示习得天赋j的一个前置天赋为i。
数据保证第一列和主对角线全为0。
n<=300
Output
第一行一个整数,问题所求的方案数。
Sample Input8 01111111 00101001 01010111 01001111 01110101 01110011 01111100 01110110Sample Output72373 Hint
其实就是问你这个有向图的生成树个数。方法依旧是利用矩阵树定理,只不过与无向图不同的是,要将度数矩阵改成入度矩阵或出度矩阵,分别对应外向树和内向树。还有删掉的那行和那列必须是根的那行那列。
A为邻接矩阵,D为度数矩阵,则基尔霍夫(Kirchhoff)
矩阵即为:K=D−A。具体实现中,记 a 为Kirchhoff矩阵,则若存在 E(u,v),
则 a[u][u]++ ,a[v][v]++,a[u][v]−−,a[v][u]−− 。即a[i][i]
为 ii 点的度数, a[i][j] 为 i,ji,j之间边的条数的相反数。
剩余系下没有除法,所以使用辗转相除高斯消元
gauss之前要把数组内所有的数全变成mod意义下的正数
#include <cstdio> #include <iostream> #include <cstring> using namespace std; typedef long long ll; const ll P=1000000007; int n; ll ans; char str[310]; int d[310][310],lj[310][310]; ll v[310][310]; int main() { scanf("%d",&n); int i,j,k; for(i=0;i<n;i++) { scanf("%s",str); for(j=0;j<n;j++) { lj[i][j]=str[j]-'0'; if(lj[i][j]) d[j][j]++; } } int ff=1; for(i=1;i<n;i++) for(j=1;j<n;j++) v[i][j]=(d[i][j]-lj[i][j]+P)%P; for(ans=1,i=1;i<n;i++) { for(j=i;j<n;j++) if(v[j][i]) break; if(j!=i) for(ff*=-1,k=i;k<n;k++) swap(v[i][k],v[j][k]); for(j=i+1;j<n;j++) { //ll A=v[i][i],B=v[j][i],tmp,temp; while(v[j][i]) { ll t=v[i][i]/v[j][i]; // tmp=A/B,temp=A,A=B,B=temp%B; for(ff*=-1,k=i;k<n;k++) v[i][k]=(v[i][k]-t*v[j][k]%P+P)%P, swap(v[i][k],v[j][k]); } } ans=ans*v[i][i]%P; } if(ff==-1 ) ans *=-1; ans %=P ;ans += P ;ans %= P; printf("%lld ",ans); return 0; }