• 矩阵树定理(Matrix Tree)学习笔记


    如果不谈证明,稍微有点线代基础的人都可以在两分钟内学完所有相关内容。。

    行列式随便找本线代书看一下基本性质就好了。

    学习资源:

    https://www.cnblogs.com/candy99/p/6420935.html

    http://blog.csdn.net/Marco_L_T/article/details/72888138

    首先是行列式对几个性质(基本上都是用数学归纳法证):

    1.交换两行(列),行列式取相反数

    2.由1.得若存在两行(列)完全相同则行列式为0

    3.上(下)三角行列式即主对角线值之积

    只有这三条用得上。

    然后就可以直接上Matrix Tree定理了:一个无向图的邻接矩阵减去度数矩阵得到的矩阵的任意n-1阶子矩阵的行列式的绝对值等于其有标号生成树的数目。

    其中邻接矩阵-度数矩阵即为基尔霍夫矩阵(又称拉普拉斯算子)。

    加强版:有向图的树形图(从根可以走到任意点)个数,将上面的“度数矩阵”改为“入度矩阵即可”(见第二份链接)

    若求以x为根的外向树数量,则求删去第x行与第x列的n-1阶子矩阵的行列式即可。

    关于行列式的算法,按定义朴素跑复杂度是阶乘级别的,利用上面的性质,初等行变换使之成为上三角矩阵即可。(实际上就是高斯消元)。注意高消中基准元要选绝对值最大的,以及注意判0退出。

    下面是几道裸题:

    SPOJ Highways  裸题

     1 #include<cmath>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define rep(i,l,r) for (int i=l; i<=r; i++)
     6 using namespace std;
     7 
     8 const int N=15;
     9 const double eps=1e-8;
    10 
    11 int T,n,m,u,v;
    12 double a[N][N];
    13 
    14 void Gauss(){
    15     n--;
    16     rep(i,1,n){
    17         int r=i;
    18         rep(j,i+1,n) if (fabs(a[j][i])>fabs(a[r][i])) r=j;
    19         if (fabs(a[r][i]<eps)) { puts("0"); return; }
    20         if (r!=i) rep(k,1,n) swap(a[r][k],a[i][k]);
    21         rep(j,i+1,n){
    22             double t=a[j][i]/a[i][i];
    23             rep(k,i,n) a[j][k]-=t*a[i][k];
    24         }
    25     }
    26     double ans=1;
    27     rep(i,1,n) ans*=a[i][i];
    28     printf("%.0f
    ",abs(ans));
    29 }
    30 
    31 int main(){
    32     for (scanf("%d",&T); T--; ){
    33         scanf("%d%d",&n,&m);
    34         memset(a,0,sizeof(a));
    35         rep(i,1,m) scanf("%d%d",&u,&v),a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;
    36         Gauss();
    37     }
    38     return 0;
    39 }

    BZOJ4766:

    先手工构造出矩阵然后观察规律求出公式。

    https://blog.sengxian.com/solutions/bzoj-4766

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 typedef long long ll;
     5 
     6 ll n,m,P;
     7 ll mod(ll x){ return (x<P) ? x : x-P; }
     8 
     9 ll mul(ll a,ll b){
    10     ll res=0;
    11     for (; b; b>>=1,a=mod(a+a))
    12         if (b & 1) res=mod(res+a);
    13     return res;
    14 }
    15 
    16 ll pow(ll a,ll b){
    17     ll res=1;
    18     for (; b; b>>=1,a=mul(a,a))
    19         if (b & 1) res=mul(res,a);
    20     return res;
    21 }
    22 
    23 int main(){
    24     scanf("%lld%lld%lld",&n,&m,&P);
    25     printf("%lld
    ",mul(pow(n,m-1),pow(m,n-1)));
    26     return 0;
    27 }

    BZOJ4031 裸题

    这里又个trick,高斯消元的时候因为模数是个合数不好求逆元,所以用辗转相除的方法做就好了。

    就是不停对两行相互进行初等行变换直到其中一行第一个数变为0,这样复杂度是log的,并且保证膜意义下不会出错。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=l; i<=r; i++)
     5 typedef long long ll;
     6 using namespace std;
     7 
     8 const int N=105,md=1000000000;
     9 int n,m,a[N][N],id[N][N],tot;
    10 char s[N][N];
    11 
    12 void Gauss(int n){
    13     int s=0;
    14     rep(i,1,n){
    15         int r=i;
    16         rep(j,i+1,n) if (a[j][i]>a[r][i]) r=j;
    17         if (!a[r][i]) { puts("0"); return; }
    18         if (i!=r){
    19             s^=1;
    20             rep(k,i,n) swap(a[i][k],a[r][k]);
    21         }
    22         rep(j,i+1,n){
    23             while (a[j][i]){
    24                 ll t=a[j][i]/a[i][i];
    25                 rep(k,i,n) a[j][k]=(a[j][k]-t*a[i][k]%md+md)%md;
    26                 if (!a[j][i]) break;
    27                 s^=1;
    28                 rep(k,i,n) swap(a[i][k],a[j][k]);
    29             }
    30         }
    31     }
    32     ll ans=1;
    33     rep(i,1,n) ans=ans*a[i][i]%md;
    34     if (s) ans=(md-ans)%md;
    35     printf("%lld
    ",ans);
    36 }
    37 
    38 void work(){
    39     rep(i,1,m) rep(j,1,n) if (s[i][j]=='.') id[i][j]=++tot;
    40     rep(i,1,m) rep(j,1,n) if (id[i][j]){
    41         int u=id[i][j],v;
    42         if (i!=1 && s[i-1][j]=='.')
    43             v=id[i-1][j],a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;
    44         if (j!=1 && s[i][j-1]=='.')
    45             v=id[i][j-1],a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;
    46     }
    47     rep(i,1,m*n) rep(j,1,m*n) a[i][j]=(a[i][j]+md)%md;
    48 }
    49 
    50 int main(){
    51     freopen("bzoj4031.in","r",stdin);
    52     freopen("bzoj4031.out","w",stdout);
    53     scanf("%d%d",&m,&n);
    54     rep(i,1,m) scanf("%s",s[i]+1);
    55     work(); Gauss(tot-1);
    56     return 0;
    57 }
  • 相关阅读:
    栅格系统
    JQuery
    week 4
    week 1
    js嵌套,BOM,DOM,内置对象,数组,自定义对象,正则表达式
    week 2
    case when的两种用法
    获取当前路径
    parse,tryparse区别
    parse ,tryparse 续
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8481592.html
Copyright © 2020-2023  润新知