zoukankan      html  css  js  c++  java
  • 【bzoj4031】[HEOI2015]小Z的房间 && 【bzoj4894】天赋 (矩阵树定理)

    来两道矩阵树模板:

    T1:【bzoj4031】[HEOI2015]小Z的房间

    Description

    你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。
    你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。

    Input

    第一行两个数分别表示n和m。
    接下来n行,每行m个字符,每个字符都会是’.’或者’*’,其中’.’代表房间,’*’代表柱子。

    Output

     一行一个整数,表示合法的方案数 Mod 10^9

    题解:

      这是一道典型的矩阵树裸题……

      不过,这取模 10^9 不是质数,不能普通的高斯消元,要使用辗转相除法。

     1 int f=1;
     2 for(int i=1;i<cnt;i++){
     3     for(int j=i+1;j<=cnt;j++){
     4         while(a[j][i]){
     5             int t=a[j][i]/a[i][i];
     6             for(int k=i;k<=cnt;k++)
     7                 a[j][k]=(a[j][k]-1LL*a[i][k]*t%mod+mod)%mod;
     8             if(a[j][i]==0)break;
     9             for(int k=i;k<=cnt;k++)swap(a[i][k],a[j][k]);
    10             f=-f;
    11         }
    12     }
    13 }

      这里涉及到交换,貌似统计答案要变为相反数……

    COMPLETE CODE:

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 
     5 #define mod 1000000000
     6 int n,m,cnt,ans=1,a[100][100],id[10][10];
     7 char s[10];
     8 
     9 int qpow(int x,int y){
    10     int ans=1;
    11     while(y){
    12         if(y&1)ans=1LL*ans*x%mod;
    13         y>>=1,x=1LL*x*x%mod;
    14     }
    15     return ans;
    16 }
    17 
    18 int main(){
    19     scanf("%d%d",&n,&m);
    20     for(int i=1;i<=n;i++){
    21         scanf("%s",s+1);
    22         for(int j=1;j<=m;j++){
    23             if(s[j]=='*')continue;
    24             id[i][j]=++cnt;
    25             if(id[i-1][j]){
    26                 a[id[i-1][j]][id[i][j]]--;
    27                 a[id[i][j]][id[i-1][j]]--;
    28                 a[id[i][j]][id[i][j]]++;
    29                 a[id[i-1][j]][id[i-1][j]]++;
    30             }if(id[i][j-1]){
    31                 a[id[i][j-1]][id[i][j]]--;
    32                 a[id[i][j]][id[i][j-1]]--;
    33                 a[id[i][j]][id[i][j]]++;
    34                 a[id[i][j-1]][id[i][j-1]]++;
    35             }
    36         }
    37     }
    38     cnt--;
    39     for(int i=1;i<=cnt;i++)
    40         for(int j=1;j<=cnt;j++)
    41             if(a[i][j]<0)a[i][j]+=mod;
    42     int f=1;
    43     for(int i=1;i<cnt;i++){
    44         for(int j=i+1;j<=cnt;j++){
    45             while(a[j][i]){
    46                 int t=a[j][i]/a[i][i];
    47                 for(int k=i;k<=cnt;k++)
    48                     a[j][k]=(a[j][k]-1LL*a[i][k]*t%mod+mod)%mod;
    49                 if(a[j][i]==0)break;
    50                 for(int k=i;k<=cnt;k++)swap(a[i][k],a[j][k]);
    51                 f=-f;
    52             }
    53         }
    54     }
    55     for(int i=1;i<=cnt;i++)
    56         ans=1LL*ans*a[i][i]%mod;
    57     printf("%d",~f?ans:(mod-ans)%mod);
    58 }

    T2:【bzoj4894】天赋

    Description

    小明有许多潜在的天赋,他希望学习这些天赋来变得更强。正如许多游戏中一样,小明也有n种潜在的天赋,但有一些天赋必须是要有前置天赋才能够学习得到的。也就是说,有一些天赋必须是要在学习了另一个天赋的条件下才能学习的。比如,要想学会"开炮",必须先学会"开枪"。一项天赋可能有多个前置天赋,但只需习得其中一个就可以学习这一项天赋。上帝不想为难小明,于是小明天生就已经习得了1号天赋-----"打架"。于是小明想知道学习完这n种天赋的方案数,答案对1,000,000,007取模。(两种方案不同指的是存在某种天赋的前置天赋不同)

    Input

    第一行一个整数n。
    接下来是一个n*n的01矩阵,第i行第j列为1表示习得天赋j的一个前置天赋为i。
    数据保证第一列和主对角线全为0。
    n<=300

    Output

    第一行一个整数,问题所求的方案数。

    题解:

      这是一道有向图生成树计数的裸题……

      没什么好说的。

    CODE:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 #define mod 1000000007
     6 char s[305];
     7 int n,a[305][305],ans=1;
     8 
     9 int qpow(int x,int y){
    10     int ans=1;
    11     while(y){
    12         if(y&1)ans=1LL*ans*x%mod;
    13         y>>=1,x=1LL*x*x%mod;
    14     }
    15     return ans;
    16 }
    17 
    18 void Gauss(){
    19     for(int i=2;i<n;i++){
    20         for(int j=i+1;j<=n;j++){
    21             if(a[j][i]==0)continue;
    22             if(a[i][i]==0){
    23                 putchar('0');
    24                 exit(0);
    25             }
    26             int tmp=1ll*a[j][i]*qpow(a[i][i],mod-2)%mod;
    27             for(int k=i;k<=n;k++)
    28                 a[j][k]=(a[j][k]-1ll*a[i][k]*tmp%mod+mod)%mod;
    29         }
    30     }
    31 }
    32 
    33 int main(){
    34     scanf("%d",&n);
    35     for(int i=1;i<=n;i++){
    36         scanf("%s",s+1);
    37         for(int j=1;j<=n;j++)
    38             if(s[j]=='1')a[j][i]=-1,a[j][j]++;
    39     }
    40     for(int i=2;i<=n;i++)
    41         for(int j=2;j<=n;j++)
    42             if(a[i][j]<0)a[i][j]+=mod;
    43     Gauss();
    44     for(int i=2;i<=n;i++)
    45         ans=1LL*ans*a[i][i]%mod;
    46     printf("%d",ans);
    47 }
  • 相关阅读:
    C++面试考点
    C++面试考点
    C++11 引用叠加规则和模板参数类型推导规则
    C++11 引用叠加规则和模板参数类型推导规则
    C++11 auto和decltype推导规则
    C++11 auto和decltype推导规则
    RVO和NRVO
    RVO和NRVO
    Strange Bank(找零问题)
    eli和字符串 (牛客假期训练)
  • 原文地址:https://www.cnblogs.com/ezoiLZH/p/9388938.html
Copyright © 2011-2022 走看看