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

87 篇文章 0 订阅

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 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
 

题目大意:

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


解题思路:

    最小生成树计数也是生成树计数问题,那么很自然的就想到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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值