码迷,mamicode.com
首页 > 其他好文 > 详细

生成树计数 Matrix-Tree 定理

时间:2019-08-27 21:14:13      阅读:66      评论:0      收藏:0      [点我收藏+]

标签:matrix   交换   const   one   计数   typedef   解法   turn   问题   

一直都知道要用Matrix-Tree定理来解决生成树计数问题,但是拖到今天才来学。博主数学不好也只能跟着各位大佬博客学一下它的应用以及会做题,证明实在是不会。

推荐博客https://blog.csdn.net/u011815404/article/details/89091011(Matrix-Tree定理)

https://blog.csdn.net/u011815404/article/details/99679527(写得无敌好的生成树计数了)

 

那么比较常见的生成树计数问题就三种:①生成树计数②同边权边较少的MST计数③同边权边较少的MST计数,针对这3个问题有3种解决办法。

最简单的生成树计数就是Matrix-Tree定理的模板题啦,直接求出Kirchhoff 矩阵然后求它的n-1阶行列式绝对值。

Highways

代码抄袭参考的大佬的。

技术图片
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=10+5;
int n,m;

LL K[N][N];
LL det(int n){  //求矩阵K的n-1阶顺序主子式
    LL ret=1;
    for(int i=1;i<=n-1;i++){  //枚举主对角线上第i个元素
        for(int j=i+1;j<=n-1;j++){  //枚举剩下的行
            while(K[j][i]){  //辗转相除
                int t=K[i][i]/K[j][i];
                for(int k=i;k<=n-1;k++)  //转为倒三角
                    K[i][k]=K[i][k]-t*K[j][k];
                swap(K[i],K[j]);  //交换i、j两行
                ret=-ret;  //取负
            }
        }
        ret=ret*K[i][i];
    }
    return abs(ret);
}

int main()
{
    int T; cin>>T;
    while (T--) {
        scanf("%d%d",&n,&m);
        memset(K,0,sizeof(K));
        for (int i=1;i<=m;i++) {
            int x,y; scanf("%d%d",&x,&y);
            K[x][x]++; K[y][y]++;
            K[x][y]--; K[y][x]--;
        }
        printf("%lld\n",det(n));
    }
    return 0;
} 
View Code

 

MST计数的话,如果同边权边较少的话可以考虑用暴力。它基于MST的两条性质:

  • 每种权值的边的数量是固定的
  • 不同的生成树中,某一种权值的边任意加入需要的数量后,形成的联通块状态是相同的

意思就是对于所有的MST对于边权例如为w的边的数量是一定的。这就启发我们暴力搜索每种边权选的使用的边,然后用乘法原理计算出方案数。这种办法因为是枚举所有的使用同边权使用情况所以时间复杂度是O(2^k*m)(k是同边权边数)。但是因为这个解法有局限性所以也不是特别有用。

去掉同边权边数限制的话就是更广泛的最小生成树计数了,要用到缩点+Matrix-Tree定理的解法。具体就是还是枚举每个边权i,然后把非边权i的边加入到图中然后缩点,对缩完点之后的图求Kirchhoff 矩阵利用Matrix-Tree定理求出生成树个数那么这个就是边权i的方案数了,然后全部边权乘法原理乘起来就是答案了。

 

洛谷P4208 最小生成树计数

给出一幅图求MST数量,缩点+Matrix-Tree定理解决。(这道题的数据也可以用暴力搜索解决)

代码还没写这里先贴个大佬的模板:

技术图片
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
const int MOD = 31011;
const int N = 1000+5;
const int dx[] = {0,0,-1,1,-1,-1,1,1};
const int dy[] = {-1,1,0,0,-1,1,-1,1};
using namespace std;
 
 
struct Edge {
    int x,y;
    int dis;
    bool operator < (const Edge &rhs) const {
        return dis<rhs.dis;
    }
} edge[N],tr[N];
int n,m;
int father[N];
int G[N][N];
int tot,bel[N],val[N];
int Find(int x) {
    if(father[x]!=x)
        return father[x]=Find(father[x]);
    return x;
}
int Gauss(int n) {
    int res=1;
    for(int i=1; i<=n; i++) {
        for(int k=i+1; k<=n; k++) {
            while(G[k][i]) {
                int d=G[i][i]/G[k][i];
                for(int j=i; j<=n; j++)
                    G[i][j]=(G[i][j]-1LL*d*G[k][j]%MOD+MOD)%MOD;
                swap(G[i],G[k]);
                res=-res;
            }
        }
        res=1LL*res*G[i][i]%MOD,res=(res+MOD)%MOD;
    }
    return res;
}
int Kruskal() {
    sort(edge+1,edge+m+1);
    for(int i=1; i<=n; i++)
        father[i]=i;
    int cnt=0;
    for(int i=1; i<=m; i++) {
        int fu=Find(edge[i].x);
        int fv=Find(edge[i].y);
        if(fu==fv)
            continue;
        father[fu]=fv,tr[++cnt]=edge[i];
        if(edge[i].dis!=val[tot])
            val[++tot]=edge[i].dis;
    }
    return cnt;
}
void addTreeEdge(int v) {
    for(int i=1; i<n&&tr[i].dis!=v; i++){
        int x=tr[i].x;
        int y=tr[i].y;
        father[Find(x)]=Find(y);
    }
    for(int i=n-1; i&&tr[i].dis!=v; i--){
        int x=tr[i].x;
        int y=tr[i].y;
        father[Find(x)]=Find(y);
    }
}
void rebuild(int v) {
    memset(G,0,sizeof(G));
    for(int i=1; i<=m; i++){
        if(edge[i].dis==v){
            int x=bel[edge[i].x];
            int y=bel[edge[i].y];
            G[x][y]--;
            G[y][x]--;
            G[x][x]++;
            G[y][y]++;
        }
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].dis);
 
    int cnt=Kruskal();
    if(cnt!=n-1) {
        printf("0\n");
    }
    else{
        int res=1;
        for(int i=1; i<=tot; i++) {
            for(int i=1; i<=n; i++)
                father[i]=i;
 
            addTreeEdge(val[i]);
 
            int blo=0;
            for(int i=1; i<=n; i++)
                if(Find(i)==i)
                    bel[i]=++blo;
            for(int i=1; i<=n; i++)
                bel[i]=bel[Find(i)];
 
            rebuild(val[i]);
            res=1LL*res*Gauss(blo-1)%MOD;
        }
        printf("%d\n",res);
    }
    return 0;
}
View Code

 

生成树计数 Matrix-Tree 定理

标签:matrix   交换   const   one   计数   typedef   解法   turn   问题   

原文地址:https://www.cnblogs.com/clno1/p/11420707.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!