Minimum Spanning Tree

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

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

【Sample Output】


【题意】

一张无向图,要求求出其中最小生成树的棵树。
【分析】

生成树计数可以使用Matrix-Tree定理解决,本题最主要的区别是有了一个最小生成树的额外条件。

首先考虑一下如何得到最小生成树。

Kruskal算法的基本思想是,按照边长排序,然后不断将短边加入集合,最终一步如果能成功把n-1条边都加入同一个集合,则找到了最小生成树。在维护集合时,可以使用并查集来快速处理。

如果把Kruskal的过程按照边长划分成多个阶段,实际上是处理了所有短边的连通性之后继续处理下一个长度的边的连通性,并依次继续处理剩下边的连通性。然后我们可以发现,不同长度的边之间的连通性互不影响!!!

假设存在n1条长度为c1的边,n2条长度为c2的边...则Kruskal首先处理c1边的连通性,然后处理c2边的连通性,对于c1边的连通性的处理可能有多种方案,即从n1条边中取出一定数量的边构成最大连通图,但是最终处理完之后的结果对于c2来说是完全一样的。因此算法就出来了,在Kruskal的基础上,使用Matrix-Tree定理处理每个阶段生成树的种数,最后将所有阶段的结果相乘即可。

具体实现为:

在Kruskal的基础上,每完成一个阶段(检查完一个长度),就将所有遍历过的点缩成一个点,然后用Matrix-Tree定理计算该点与下一组点组成的连通图中生成树的个数。最终把每一个阶段的结果相乘即可。

 /* ***********************************************
MYID : Chen Fan
LANG : G++
PROG : Counting_MST_HDU4408
************************************************ */ #include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bitset>
#define N 405
#define M 4005 using namespace std; typedef struct nod
{
int a,b,c;
} node;
node edge[M]; bool op(node a,node b)
{
return a.c<b.c;
} int n,m,o,fa[N],ka[N];
long long ans,mod,gk[N][N],kir[N][N]; bitset<N> flag;
vector<int> gra[N]; int getfather(int x,int father[])
{
if (father[x]!=x) father[x]=getfather(father[x],father);
return father[x];
} long long det(long long a[][N],int n) //Matrix-Tree定理求Kirchhoff矩阵
{
for (int i=;i<n;i++)
for (int j=;j<n;j++) a[i][j]%=mod;
long long ret=;
for (int i=;i<n;i++)
{
for (int j=i+;j<n;j++)
while (a[j][i])
{
long long t=a[i][i]/a[j][i];
for (int k=i;k<n;k++) a[i][k]=(a[i][k]-a[j][k]*t)%mod;
for (int k=i;k<n;k++) swap(a[i][k],a[j][k]);
ret=-ret;
}
if (a[i][i]==) return ;
ret=ret*a[i][i]%mod;
//ret%=mod;
}
return (ret+mod)%mod;
} void matrix_tree()
{
for (int i=;i<=n;i++) //根据访问标记找出连通分量
if (flag[i])
{
gra[getfather(i,ka)].push_back(i);
flag[i]=;
}
for (int i=;i<=n;i++)
if (gra[i].size()>) //枚举连通分量
{
memset(kir,,sizeof(kir)); int len=gra[i].size();
for (int a=;a<len;a++)
for (int b=a+;b<len;b++)
{
int la=gra[i][a],lb=gra[i][b];
kir[b][a]-=gk[la][lb];
kir[a][b]=kir[b][a];
kir[a][a]+=gk[la][lb];
kir[b][b]+=gk[la][lb];
} //构造矩阵 long long ret=det(kir,len);
ret%=mod;
ans=(ans*ret%mod)%mod; for (int a=;a<len;a++) fa[gra[i][a]]=i;
}
for (int i=;i<=n;i++) //连通图缩点+初始化
{
fa[i]=getfather(i,fa);
ka[i]=fa[i];
gra[i].clear();
}
} int main()
{
freopen("4408.txt","r",stdin); while (scanf("%d%d%lld",&n,&m,&mod)==)
{ if (n==&&m==&&mod==) break;
for (int i=;i<=m;i++) scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].c);
sort(&edge[],&edge[m+],op); for (int i=;i<=n;i++) gra[i].clear();
for (int i=;i<=n;i++)
{
fa[i]=i;
ka[i]=i;
}
flag.reset();
memset(gk,,sizeof(gk));
ans=;
o=edge[].c;
for (int i=;i<=m;i++)
{
int pa=getfather(edge[i].a,fa),pb=getfather(edge[i].b,fa);
if (pa!=pb)
{
flag[pa]=;
flag[pb]=; //访问标记
ka[getfather(pa,ka)]=getfather(pb,ka);
gk[pa][pb]++;
gk[pb][pa]++; //邻接矩阵
}
if (i==m||edge[i+].c!=o) //所有相同的边并成一组
{
matrix_tree();
o=edge[i+].c;
}
} bool done=true;
for (int i=;i<=n;i++)
if(ka[i]!=ka[i-])
{
done=false;
break;
}
if (!done) printf("0\n");
else
{
ans%=mod;
printf("%lld\n",ans);
}
} return ;
}

HDU 4408 Minimum Spanning Tree 最小生成树计数的更多相关文章

  1. hdu 4408 Minimum Spanning Tree

    Problem Description XXX is very interested in algorithm. After learning the Prim algorithm and Krusk ...

  2. 数据结构与算法分析–Minimum Spanning Tree(最小生成树)

    给定一个无向图,如果他的某个子图中,任意两个顶点都能互相连通并且是一棵树,那么这棵树就叫做生成树(spanning tree). 如果边上有权值,那么使得边权和最小的生成树叫做最小生成树(MST,Mi ...

  3. 【算法】关于图论中的最小生成树(Minimum Spanning Tree)详解

    本节纲要 什么是图(network) 什么是最小生成树 (minimum spanning tree) 最小生成树的算法 什么是图(network)? 这里的图当然不是我们日常说的图片或者地图.通常情 ...

  4. 【HDU 4408】Minimum Spanning Tree(最小生成树计数)

    Problem Description XXX is very interested in algorithm. After learning the Prim algorithm and Krusk ...

  5. Educational Codeforces Round 3 E. Minimum spanning tree for each edge 最小生成树+树链剖分+线段树

    E. Minimum spanning tree for each edge time limit per test 2 seconds memory limit per test 256 megab ...

  6. 说说最小生成树(Minimum Spanning Tree)

    minimum spanning tree(MST) 最小生成树是连通无向带权图的一个子图,要求 能够连接图中的所有顶点.无环.路径的权重和为所有路径中最小的. graph-cut 对图的一个切割或者 ...

  7. 最小生成树(Minimum Spanning Tree)——Prim算法与Kruskal算法+并查集

    最小生成树——Minimum Spanning Tree,是图论中比较重要的模型,通常用于解决实际生活中的路径代价最小一类的问题.我们首先用通俗的语言解释它的定义: 对于有n个节点的有权无向连通图,寻 ...

  8. 多校 HDU - 6614 AND Minimum Spanning Tree (二进制)

    传送门 AND Minimum Spanning Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 ...

  9. CF# Educational Codeforces Round 3 E. Minimum spanning tree for each edge

    E. Minimum spanning tree for each edge time limit per test 2 seconds memory limit per test 256 megab ...

随机推荐

  1. 网站的性能优化与安全(高效C#编码优化)

    1. Foreach 比 For 性能高30%2. 避免是使用ArrayList, 因为任何对象到ArrayList都有封装为Object,出来还要拆箱.    用泛型去掉3. HashTalbe取代 ...

  2. 使用TcpTrace小工具截获Web Service的SOAP报文

    Web Service客户端对服务端进行调用时,请求和响应都使用SOAP报文进行通讯.在开发和测试时,常常查看SOAP报文的内容,以便进行分析和调试.TcpTrace是一款比较小巧的工具,可以让我们截 ...

  3. nginx优化缓冲缓存

    反向代理的一个问题是代理大量用户时会增加服务器进程的性能冲击影响.在大多数情况下,可以很大程度上能通过利用Nginx的缓冲和缓存功能减轻. 当代理到另一台服务器,两个不同的连接速度会影响客户的体验: ...

  4. android把图片 视频 保存到相册

    //android把图片文件添加到相册 ContentResolver localContentResolver = getContentResolver(); ContentValues local ...

  5. eclipse 终极操作技巧

    eclipse作为一个java开发必备软件,从用户体验来说,还是蛮一般的(按照初始设置的话),所以有必要进行一些设置上的改良,加上对一些好用的快捷键的挖掘,能让你用eclipse更加得心应手,事半功倍 ...

  6. OpenGL ES2.0入门详解

    引自:http://blog.csdn.net/wangyuchun_799/article/details/7736928  1.决定你要支持的OpenGL ES的版本.目前,OpenGL ES包含 ...

  7. MFC 透明内存DC

    在MFC中绘制比较复杂图形,通常采用双缓冲技术来绘图,的确可以大大加快绘制速度和减少闪烁,但是有些情况也不尽然. 我最近遇到了一个问题,采用的也是双缓冲来加快绘图,但是绘制效果还是不尽人意.A对象里大 ...

  8. NSAttributedString 的21种属性 详解

    原文链接:http://www.jianshu.com/p/09f54730feaa 先看看所有的Key NSFontAttributeName; //字体,value是UIFont对象 NSPara ...

  9. DHCPv6

    SLAAC(RFC4862)(StatelessAddressAutoconfiguration),无状态自动配置 IT网,http://www.it.net.cn DHCPv6包含以下两种形式: n ...

  10. java int和Integer的区别

    今天偶然和同学讨论到int和Integer的区别是,发现自己对这个问题了解的并不是很清楚,而且有些概念还是错的,所以在这对int和Integer的区别做一个总结. int与integer的区别从大的方 ...