HDU 4408 最小生成树计数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/du_lun/article/details/82799742

传送门

思路:首先需要了解Matrix_Tree定理

1.设 G是无向图的邻接表,D是无向图各个点的度数

2.令M=D-G,则M的任意n-1阶余子式的行列式值的 绝对值, 就是无向图生成树的个数

应注意的是邻接表的值不只是0,1值,也就是说,当图出现重边的话,邻接表该位置累加。

这道题思路,对于最小生成树,克鲁斯卡尔算法是将边从小到大排序,对于同一大小的边,

在最小生成树中用到的数量是一定的(这个需要好好理解),这样的话,我们每次只需要将满足条件的

同样大小的边统计一下,这样会产生多个联通分量,对每个联通分量 用Matrix_Tree计数。

生成树的点是之前每个联通分量的缩点,固需要 两个并查集。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110;
int n, m, mo, fa[N], ka[N], lin[N][N];
ll ans, M[N][N];
bitset<N> vis;
struct Edge{
    int u, v, w;
    bool operator <(const Edge &b) const{
        return w<b.w;
    }
}e[N*10];

void init(){
    ans=1;
    for(int i=1; i<=n; i++){
        fa[i]=ka[i]=i;
    }
}

int _find(int f[], int r){
    return f[r]==r? r:f[r]=_find(f, f[r]);
}

/*行列式求值模板,将行列式化成上三角行列式,对角线乘积就是答案, 复杂度O(n^3)多一点*/
ll det(int n){
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++) M[i][j]%=mo;
    ll ret=1;
    for(int i=1; i<n; i++){

        for(int j=i+1; j<n; j++)
        while(M[j][i]){
            ll t=M[i][i]/M[j][i];
            for(int k=i; k<n; k++) M[i][k]=(M[i][k]-t*M[j][k])%mo;
            for(int k=i; k<n; k++) swap(M[i][k], M[j][k]);
            ret=-ret;
        }
        if(M[i][i]==0) return 0ll;
        ret=ret*M[i][i]%mo;
    }
    return (ret+mo)%mo;
}
/***********************************/
void Matrix_Tree(){

    vector<int> G[N];
    for(int i=1; i<=n; i++)if(vis[i]){//划分连通分量
        G[_find(ka, i)].push_back(i);
        vis[i]=0;
    }

    for(int i=1; i<=n; i++)if(G[i].size()){
        for(int a=0; a<n; a++) for(int b=0; b<n; b++) M[a][b]=0;
        int len=G[i].size();
        for(int j=0; j<len; j++){
            for(int k=j+1; k<len; k++){
                int u=G[i][j], v=G[i][k];
                M[j][j]+=lin[u][v];
                M[k][k]+=lin[u][v];
                M[k][j]-=lin[u][v];
                M[j][k]=M[k][j];
            }
        }
        ans=(ans*det(len))%mo;
    }
    for(int i=1; i<=n; i++)
        fa[i]=_find(ka, i);
}

int main(){
    while(~scanf("%d%d%d", &n, &m, &mo)&&(n||m||mo)){
        for(int i=1; i<=m; i++)
            scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
        init();

        sort(e+1, e+1+m);
        int pre=e[1].w;
        vis.reset();
        memset(lin, 0, sizeof lin);
        for(int i=1; i<=m; i++){

            int u=e[i].u, v=e[i].v;
            int tu=_find(fa, u), tv=_find(fa, v);
            if(tu!=tv){
                vis[tu]=1; vis[tv]=1;
                ka[_find(ka, tu)]=_find(ka, tv);
                lin[tu][tv]++; lin[tv][tu]++;
            }
            if(i==m || pre!=e[i+1].w){
                Matrix_Tree();
                pre=e[i+1].w;
            }
        }
        bool ok=true;
        for(int i=2; i<=n; i++)
            if(fa[i]!=fa[i-1]){
                ok=false;
                break;
            }

        if(!ok)
            printf("0\n");
        else
            printf("%lld\n", (ans+mo)%mo);

    }
    return 0;
}

/*
6 6 100
1 2 1
2 3 1
3 4 1
4 1 1
4 5 2
5 6 3
*/

猜你喜欢

转载自blog.csdn.net/du_lun/article/details/82799742
今日推荐