【HDU 4408】Minimum Spanning Tree(最小生成樹計數)


Problem Description
XXX is very interested in algorithm. After learning the Prim algorithm and Kruskal algorithm of minimum spanning tree, XXX finds that there might be multiple solutions. Given an undirected weighted graph with n (1<=n<=100) vertexes and m (0<=m<=1000) edges, he wants to know the number of minimum spanning trees in the graph.
 

 

Input
There are no more than 15 cases. The input ends by 0 0 0.
For each case, the first line begins with three integers --- the above mentioned n, m, and p. The meaning of p will be explained later. Each the following m lines contains three integers u, v, w (1<=w<=10), which describes that there is an edge weighted w between vertex u and vertex v( all vertex are numbered for 1 to n) . It is guaranteed that there are no multiple edges and no loops in the graph.
 

 

Output
For each test case, output a single integer in one line representing the number of different minimum spanning trees in the graph.
The answer may be quite large. You just need to calculate the remainder of the answer when divided by p (1<=p<=1000000000). p is above mentioned, appears in the first line of each test case.
 

 

Sample Input
5 10 12
2 5 3
2 4 2
3 1 3
3 4 2
1 2 3
5 4 3
5 1 3
4 1 1
5 3 3
3 2 3
0 0 0
 

 

Sample Output
4
 

 

Source
 

 

Recommend
zhoujiaqi2010   |   We have carefully selected several similar problems for you:   5831  5830  5829  5828  5827 

題意是給定n個點,m條邊的無向圖,求最小生成樹的個數對p取模。

用kruscal計算最小生成樹時,每次取連接了兩個不同聯通塊的最小的邊。也就是先處理d1條c1長度的邊,再處理d2條c2長度的邊。長度相同的邊無論怎么選,最大聯通情況都是固定的。 分別對ci長度的邊產生的幾個聯通塊計算生成樹數量再乘起來,然后把這些聯通塊縮點,再計算ci+1長度的邊。

生成樹計數用Matrix-Tree定理,上一篇是無重邊的,這題的縮點后是會產生重邊的,Matrix-tree也適用:

Kirchhoff矩陣任意n-1階子矩陣的行列式的絕對值就是無向圖的生成樹的數量。

Kirchhoff矩陣的定義是度數矩陣-鄰接矩陣。

1、G的度數矩陣D[G]:n*n的矩陣,Dii等於Vi的度數,其余為0。
2、G的鄰接矩陣A[G]:n*n的矩陣, Vi、Vj之間有邊直接相連,則 Aij=ij之間的邊數,否則為0。

並查集fa[i]是當前長度之前,節點所屬的聯通塊,ka[i]是當前長度的邊連接后它在的聯通塊。

 

 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N=101; 
const int M=1001;
ll n,m,p,ans;
vector<int>gra[N];
struct edge{
    int u,v,w;
}e[M];
int cmp(edge a,edge b){
    return a.w<b.w;
}
ll mat[N][N],g[N][N];
ll fa[N],ka[N],vis[N];
ll det(ll c[][N],ll n){
    ll i,j,k,t,ret=1;
    for(i=0;i<n;i++)
    for(j=0;j<n;j++) c[i][j]%=p;
    for(i=0; i<n; i++){
        for(j=i+1; j<n; j++)
            while(c[j][i]){
                t=c[i][i]/c[j][i];
                for(k=i; k<n; k++)
                    c[i][k]=(c[i][k]-c[j][k]*t)%p;
                swap(c[i],c[j]);
                ret=-ret;
            }
        if(c[i][i]==0)
            return 0L;
        ret=ret*c[i][i]%p;
    }
    return (ret+p)%p;
}
ll find(ll a,ll f[]){
    return f[a]==a?a:find(f[a],f);
}
void matrix_tree(){//對當前長度的邊連接的每個聯通塊計算生成樹個數
    for(int i=0;i<n;i++)if(vis[i]){//當前長度的邊連接了i節點
        gra[find(i,ka)].push_back(i);//將i節點壓入所屬的聯通塊
        vis[i]=0;//一邊清空vis數組
    }
    for(int i=0;i<n;i++)
    if(gra[i].size()>1){//聯通塊的點數為1時生成樹數量是1
        memset(mat,0,sizeof mat);//清空矩陣
        int len=gra[i].size();
        for(int j=0;j<len;j++)
        for(int k=j+1;k<len;k++){//構造這個聯通塊的矩陣(有重邊)
            int u=gra[i][j],v=gra[i][k];
            if(g[u][v]){
                mat[k][j]=(mat[j][k]-=g[u][v]);
                mat[k][k]+=g[u][v];mat[j][j]+=g[u][v];
            }
        }
        ans=ans*det(mat,gra[i].size()-1)%p;
        for(int j=0;j<len;j++)fa[gra[i][j]]=i;//縮點
    }
    for(int i=0;i<n;i++)
    {
        gra[i].clear();
        ka[i]=fa[i]=find(i,fa);
    }
}
int main(){
    while(scanf("%lld%lld%lld",&n,&m,&p),n){
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u--;v--;
            e[i]=(edge){u,v,w};
        }
        sort(e,e+m,cmp);
        memset(g,0,sizeof g);
        ans=1;
        for(ll i=0;i<n;i++)ka[i]=fa[i]=i;
        for(ll i=0;i<=m;i++){//邊從小到大加入
            if(i&&e[i].w!=e[i-1].w||i==m)//處理完長度為e[i-1].w的所有邊
                matrix_tree();//計算生成樹
            ll u=find(e[i].u,fa),v=find(e[i].v,fa);//連的兩個縮點后的點
            if(u!=v)//如果不是一個
            {
                vis[v]=vis[u]=1;
                ka[find(u,ka)]=find(v,ka);//兩個分量在一個聯通塊里。
                g[u][v]++,g[v][u]++;//鄰接矩陣
            }
        }
        int flag=1;
        for(int i=1;i<n;i++)if(fa[i]!=fa[i-1])flag=0;
        printf("%lld\n",flag?ans%p:0);//注意p可能為1,這樣m=0時如果ans不%p就會輸出1
    }
}

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM