Bzoj4894 天赋

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 16  Solved: 16

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

Sample Output

72373

HINT

 

Source

 

图论 基尔霍夫矩阵 高斯消元

把“点了一个前置技能(或一个前置相同的同级技能),接着点当前技能”看成一条有向边,那么我们要求的就是这个有向图的树形图的数量。

 

(度娘告诉我)有向图也可以用矩阵树定理搞。

敲完以后跑样例,怎么调答案都是0

输出矩阵看了一下,发现最左边一列全是0,然后注意到树形图的根固定了是1,也许这导致矩阵实际上少了一个元?

暴力把矩阵往左上平移一单位,再求出行列式,发现就是答案。

 

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #define LL long long
 7 using namespace std;
 8 const int mod=1e9+7;
 9 const int mxn=305;
10 int f[mxn][mxn];
11 int n;
12 char s[mxn];
13 int ksm(int a,int k){
14     int res=1;
15     while(k){
16         if(k&1)res=(LL)res*a%mod;
17         a=(LL)a*a%mod;
18         k>>=1;
19     }
20     return res;
21 }
22 int solve(int n){
23     int i,j,k,F=1;
24     for(i=1;i<=n;i++){
25         int p=i;
26         for(j=i;j<=n;j++)if(f[j][i]){p=i;break;}
27         if(p!=i){for(j=1;j<=n;j++)swap(f[p][j],f[i][j]); F=-F;}
28         for(j=i+1;j<=n;j++){
29             LL inv=(LL)f[j][i]*ksm(f[i][i],mod-2)%mod;
30             for(k=i;k<=n;k++){
31                 f[j][k]=((LL)f[j][k]-inv*(LL)f[i][k]%mod+mod)%mod;
32             }
33         }
34     }
35     int res=F;
36     for(i=1;i<=n;i++)res=(LL)res*f[i][i]%mod;
37     if(res<0)res+=mod;
38     return res;
39 }
40 int main(){
41     int i,j;
42     scanf("%d",&n);
43     for(i=1;i<=n;i++){
44         scanf("%s",s+1);
45         for(j=1;j<=n;j++){
46             if(s[j]=='1'){
47                 f[j][j]++;
48                 f[i][j]--;
49             }
50         }
51     }
52     for(i=1;i<=n;i++)
53         for(j=1;j<=n;j++)
54             f[i][j]=f[i+1][j+1];
55     LL ans=solve(n-1);
56     printf("%lld\n",ans);
57     return 0;
58 }

 

posted @ 2017-05-15 22:20  SilverNebula  阅读(172)  评论(0编辑  收藏  举报
AmazingCounters.com