1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 7244 Solved: 2991
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
解析:
在网上搜题解一大堆矩阵树定理,然而我觉得完全不用说得这么高深啊,%hzw。
做这道题之前,有必要知道最小生成树的性质:
(1)不同的最小生成树中,每种权值的边出现的个数是确定的
(2)不同的生成树中,某一种权值的边连接完成后,形成的联通块状态是一样的
对于性质1,可以这样理解,加入一条边后,要使得整个图仍是最小生成树,必然会删掉与这条边权值相同的边,于是得证。
对于性质2,我认为是显然的。。。
于是我们可以把每种权值的处理看成是分开的好几步,然后根据乘法原理,将每一步得到的结果相乘。 解题步骤如下:
1.做一遍最小生成树,统计每种权值的边使用的数量
2.然后对于每一种权值的边搜索,得出每一种权值的边选择方案
3,乘法原理
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=1005;
const int mod=31011;
int n,m,size,s,tot=0,ans=1;
long long sum;
int father[105];
struct shu{int x,y,len;};
shu edge[Max];
struct tree{int l,r,num,id;};
tree a[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline bool comp(const shu &a,const shu &b){return a.len < b.len;}
inline int getfather(int v){return father[v]==v ? v : getfather(father[v]);}
inline void dfs(int i,int p,int num)
{
if(p == a[i].r+1)
{
if(num == a[i].num) sum++;
return;
}
int fax = getfather(edge[p].x),fay = getfather(edge[p].y);
if(fax != fay)
{
father[fay] = fax;
dfs(i,p+1,num+1);
father[fay] = fay,father[fax] = fax;
}
dfs(i,p+1,num); //注意不能用else,因为这里的意思是不选择这条边,每条边都有这种情况。
}
int main()
{
freopen("lx.in","r",stdin);
n=get_int();
m=get_int();
for(int i=1;i<=n;i++) father[i] = i;
for(int i=1;i<=m;i++) edge[i].x=get_int(),edge[i].y=get_int(),edge[i].len=get_int();
sort(edge+1,edge+m+1,comp);
for(int i=1;i<=m;i++)
{
int fax=getfather(edge[i].x),fay=getfather(edge[i].y);
if(edge[i].len != edge[i-1].len) a[++tot].l=i,a[tot-1].r=i-1;
if(fax != fay)
{
s++;
father[fay] = fax;
a[tot].num++;
}
}
if(s != n-1) {putchar('0');return 0;}
a[tot].r = m;
for(int i=1;i<=n;i++) father[i] = i;
for(int i=1;i<=tot;i++)
{
sum=0;
dfs(i,a[i].l,0);
if(sum) ans=(long long)(ans * sum) % mod;
for(int j=a[i].l;j<=a[i].r;j++)
{
int fax = getfather(edge[j].x),fay = getfather(edge[j].y);
if(fax != fay) father[fay] = fax;
}
}
printf("%d\n",ans);
return 0;
}