HDU 4408 Minimum Spanning Tree(最小生成树计数)

来源:互联网 发布:linux认证考试报名 编辑:程序博客网 时间:2024/05/17 23:28

Minimum Spanning Tree

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2033    Accepted Submission(s): 686


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 122 5 32 4 23 1 33 4 21 2 35 4 35 1 34 1 15 3 33 2 30 0 0
 

Sample Output
4
 

Source
2012 ACM/ICPC Asia Regional Jinhua Online 
 

Recommend
zhoujiaqi2010
 

题目大意:

    给你一个带权无向图,问有多少棵最小生成树。


解题思路:

    最小生成树计数也是生成树计数问题,那么很自然的就想到Matrix-Tree定理。Matrix-Tree定理解决的是在一张图中任意构造生成树的方案数,最小生成树中不同的情况是来源于在权值一样联通的边中可以任意选。于是我们就可在Kruskal的过程中,对于每组权值相同的边中,就可对每个联通块使用Matrix-Tree把结果乘起来就是最小生成树的方案数。

    虽然题目说输入没有重边,但缩点后是有可能有重边的。对于重边构造Kirchhoff矩阵时只要像其它边一样正常加减即可,Matrix-Tree对于重边仍然有效。


AC代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <ctime>#include <vector>#include <queue>#include <stack>#include <deque>#include <string>#include <map>using namespace std;#define INF 0x3f3f3f3f#define LL long long#define fi first#define se second#define mem(a,b) memset((a),(b),sizeof(a))const LL MAXV=100+3;const LL MAXE=1000+3;LL V,E,MOD;LL par[MAXV],high[MAXV];vector<LL> save;//当前联通块包含的边LL id[MAXV];//对每个权值相同的联通块的点重新编号后的编号LL ans;struct Edge{    LL u,v,cost;    bool operator < (const Edge& other)const    {        return cost<other.cost;    }}edge[MAXE];struct Matrix{    LL a[MAXV][MAXV];    LL det(LL n)//求前n行n列的行列式的值    {        for(LL i=0;i<n;++i)            for(LL j=0;j<n;++j)                a[i][j]=(a[i][j]%MOD+MOD)%MOD;        LL ret=1;        for(LL i=0;i<n;i++)        {            for(LL j=i+1;j<n;j++)                while(a[j][i])                {                    LL t=a[i][i]/a[j][i];                    for(LL k=i;k<n;++k)                        a[i][k]=((a[i][k]-a[j][k]*t)%MOD+MOD)%MOD;                    for(LL k=i;k<n;++k)                        swap(a[i][k],a[j][k]);                    ret=-ret;                }            if(!a[i][i])                return 0;            ret=ret*a[i][i]%MOD;        }        ret=(ret%MOD+MOD)%MOD;        return ret;    }}mat;void init(){    for(LL i=0;i<V;++i)    {        par[i]=i;        high[i]=0;    }    ans=1;}LL findfather(LL x){    return par[x]=par[x]==x?x:findfather(par[x]);}bool unite(LL u,LL v){    LL fa=findfather(u),fb=findfather(v);    if(fa==fb)        return false;    if(high[fa]>high[fb])        par[fb]=fa;    else    {        par[fa]=fb;        if(high[fa]==high[fb])            ++high[fb];    }    return true;}void matrix_tree(LL l,LL r){    for(LL i=l;i<r;++i)        unite(edge[i].u,edge[i].v);    for(int root=0;root<V;++root)//枚举每个联通分量    {        save.clear();        for(LL i=l;i<r;++i)            if(edge[i].u!=edge[i].v&&(findfather(edge[i].u)==root||findfather(edge[i].v)==root))            {                save.push_back(edge[i].u);                save.push_back(edge[i].v);            }        if(save.size()==0)            continue;        sort(save.begin(),save.end());        save.erase(unique(save.begin(), save.end()),save.end());//离散化,重新编号        for(LL i=0;i<save.size();++i)            id[save[i]]=lower_bound(save.begin(), save.end(), save[i])-save.begin();        for(int i=0;i<save.size();++i)//初始化矩阵            for(int j=0;j<save.size();++j)                mat.a[i][j]=0;        for(LL i=l;i<r;++i)//构造Kirchhoff矩阵            if(edge[i].u!=edge[i].v&&(findfather(edge[i].u)==root||findfather(edge[i].v)==root))            {                LL u=id[edge[i].u],v=id[edge[i].v];                --mat.a[u][v];                --mat.a[v][u];                ++mat.a[u][u];                ++mat.a[v][v];            }        ans=(ans*mat.det(save.size()-1))%MOD;    }    for(LL i=r;i<E;++i)//缩点    {        edge[i].u=findfather(edge[i].u);        edge[i].v=findfather(edge[i].v);    }}int main(){    while(~scanf("%lld%lld%lld",&V,&E,&MOD)&&(V||E||MOD))    {        init();        for(LL i=0;i<E;++i)        {            scanf("%lld%lld%lld",&edge[i].u,&edge[i].v,&edge[i].cost);            --edge[i].u;            --edge[i].v;        }        sort(edge,edge+E);        LL last=0;        for(LL i=0;i<=E;++i)        {            if((i&&edge[i].cost!=edge[i-1].cost)||i==E)//找到每组权值相同的边            {                matrix_tree(last,i);                last=i;            }        }        for(int i=1;i<V;++i)//检查是否联通            if(findfather(i)!=findfather(0))            {                ans=0;                break;            }        printf("%lld\n",ans%MOD);    }        return 0;}

阅读全文
0 0
原创粉丝点击