[bzoj4894]天赋

来自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\n",ans);
    return 0;
}
posted @ 2017-06-30 20:53  FallDream  阅读(342)  评论(0编辑  收藏  举报