Description
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对31011的模就可以了。
Input
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整
数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
题解:
先按照任意顺序对等长的边进行排序;
然后利用并查集将所有长度为L的边的处理当作一个整体看待;
可以定义一个vector来保存每一个连通块的边的信息;
即将原图划分成多个连通块,每个连通块里面的边的权值都相同;
针对每一个连通块构建对应的Kirchhoff矩阵C,利用Matrix_Tree定理求每一个连通块的生成树个数; 最后把他们的值相乘即可;
*Matrix_Tree定理:
G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值;n-1阶主子式就是对于r(1≤r≤n),将C[G]的第r行,第r列同时去掉后得到的新矩阵,用Cr[G]表示;
另一种做法:由于权值小于等于10,直接2^10枚举权值,暴力看是否有MST,复杂度差不多能过。
代码如下:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define mod 1000003
using namespace std;
struct edge
{
int u,v,w,x;
edge(){}
edge(int a,int b,int c,int d):u(a),v(b),w(c),x(d){}
inline bool operator< (const edge &rhs) const{return x < rhs.x;}
}e[100005];
struct count
{
int l,r,cnt;
}g[100005];
int n,m,fa[50005],siz[50005];
int find(int x){return fa[x]==x?x:find(fa[x]);}
void unio(int u,int v)
{
if(siz[u]>siz[v]) fa[v]=u,siz[u]+=siz[v];
else fa[u]=v,siz[v]+=siz[u];
}
bool kruskal()
{
int cnt=0,u,v;
for(int i=1;i<=m;i++)
{
u=find(e[i].u),v=find(e[i].v);
if(u!=v)
{
unio(u,v);
++g[e[i].w].cnt;
if(++cnt==n-1) return 1;
}
}
return 0;
}
int dfs(int w,int i,int k)
{
if(k==g[w].cnt) return 1;
if(i>g[w].r) return 0;
int ans=0,u=find(e[i].u),v=find(e[i].v);
if(u!=v)
{
unio(u,v);
ans=dfs(w,i+1,k+1);
fa[u]=u,fa[v]=v;
}
return ans+dfs(w,i+1,k);
}
int main()
{
int u,v,w,ans;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
e[i]=edge(u,v,0,w);
}
sort(e+1,e+m+1);
w=0;
for(int i=1;i<=m;i++)
{
if(e[i].x==e[i-1].x) e[i].w=w;
else
{
g[w].r=i-1;
e[i].w=++w;
g[w].l=i;
}
}
g[w].r=m;
ans=kruskal();
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
for(int i=1;i<=w;i++)
{
ans=ans*dfs(i,g[i].l,0)%mod;
for(int j=g[i].l;j<=g[i].r;j++)
{
u=find(e[j].u),v=find(e[j].v);
if(u!=v) unio(u,v);
}
}
printf("%d\n",ans);
return 0;
}