您的位置:首页 > 其它

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

2017-07-08 14:09 585 查看


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

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: