来自FallDream的博客,未经允许,请勿转载,谢谢。
小明有许多潜在的天赋,他希望学习这些天赋来变得更强。正如许多游戏中一样,小明也有n种潜在的天赋,但有一些天赋必须是要有前置天赋才能够学习得到的。也就是说,有一些天赋必须是要在学习了另一个天赋的条件下才能学习的。比如,要想学会"开炮",必须先学会"开枪"。一项天赋可能有多个前置天赋,但只需习得其中一个就可以学习这一项天赋。上帝不想为难小明,于是小明天生就已经习得了1号天赋-----"打架"。于是小明想知道学习完这n种天赋的方案数,答案对1,000,000,007取模。
n<=300
出题人的语文水平不是很好 两种方案不同其实是有一个天赋的前置天赋不同。
这样就变成了树形图计数,矩阵数定理,高斯消元即可。
复杂度O(n^3)
#include<iostream> #include<cstdio> #define MN 300 #define mod 1000000007 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } char st[MN+5][MN+5]; int n,s[MN+5][MN+5],ans=1; int pow(int x,int k) { int sum=1; for(;k;k>>=1,x=1LL*x*x%mod) if(k&1) sum=1LL*sum*x%mod; return sum; } void Gauss(int N) { for(int i=2;i<=N;++i) { for(int j=i;j<=N;++j) if(s[j][i]) { if(j!=i) { ans=mod-ans; for(int k=i;k<=N;++k) swap(s[j][k],s[i][k]); } break; } int Inv=pow(s[i][i],mod-2); for(int j=i+1;j<=N;++j) if(s[j][i]) { int inv=1LL*Inv*s[j][i]%mod; for(int k=i;k<=N;++k) s[j][k]=(s[j][k]-1LL*inv*s[i][k]%mod+mod)%mod; } } for(int i=2;i<=N;++i) ans=1LL*ans*s[i][i]%mod; } int main() { n=read();if(n==1) return 0*puts("1"); for(int i=1;i<=n;++i) { scanf("%s",st[i]+1); for(int j=1;st[i][j];++j) if(st[i][j]=='1') ++s[j][j],(s[i][j]+=mod-1)%=mod; } Gauss(n); printf("%d ",ans); return 0; }