Kruskal+dfs - 最小生成树计数 BZOJ1016[JSOI2008]

描述
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的 最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生 成树可能很多,所以你只需要输出方案数对31011的模就可以了。

输入
 第一行包含两个数,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条。

输出
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

样例输入
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
样例输出
8


Analysis

黄学长的一句话题解就没有一次性理解透过)

首先我们来分析样例,发现喵喵喵那么多的1怎么选啊?
看一波数据范围,相同权值的边最多只有10条,敢情210可以爆搜出选取方案
然而有什么用呢?难不成要21000
不,当然不是
面对最小生成树,就得拿出我们的老把戏–Kruskal
我们发现一个性质(自然不是我发现的):
对于一个图的不同最小生成树,每种方案所包含的每种权值的边的数量一定一致。
换句话说,把每种方案包含的所有边的边权都写下来,写出来的序列一定都一样。

既然这样,我们先做一遍Kruskal,记录下每种权值的边用了多少条
然后暴力dfs(210)来找合法的选择方案
最后运用乘法原理
over ovo

Little tips
由于这道题dfs时需要回溯,也就是说要把每个点的父亲改为自己
我们在find函数里就不能进行路径压缩的处理(反正也慢不到哪里去)


Code

/*created by xly*/

#include<bits/stdc++.h>
#define in read()
#define re register
#define ll long long 
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
}
const int p=31011,M=1e3+10;
int n,m,cnt=0;
int fa[105],sum=0;
inline int getfa(int x){return x==fa[x]?x:getfa(fa[x]);}
struct edge{
	int u,v,w;
	inline bool operator < (const edge &b)const{
		return w<b.w;
	}
}e[M];
struct node{int l,r,t;}a[M];
void Dfs(int tp,int now,int k){
	if(now==a[tp].r+1){
		if(k==a[tp].t) sum++;
		return ;
	}
	int u=getfa(e[now].u),v=getfa(e[now].v);
	if(u!=v){
		fa[u]=v;
		Dfs(tp,now+1,k+1);
		fa[u]=u;fa[v]=v;
	}
	Dfs(tp,now+1,k);
}
int main(){
	n=in;m=in;
	for(re int i=1;i<=n;++i) fa[i]=i;
	for(re int i=1;i<=m;++i){e[i].u=in;e[i].v=in;e[i].w=in;	}
	sort(e+1,e+m+1);int tot=0;
	for(re int i=1;i<=m;++i){
		if(e[i].w!=e[i-1].w) {a[++cnt].l=i;a[cnt-1].r=i-1;		}
		int u=getfa(e[i].u),v=getfa(e[i].v);
		if(u!=v){fa[u]=v;a[cnt].t++;tot++;		}
	}
	a[cnt].r=m;
	if(tot!=n-1) return printf("0"),0;
	for(re int i=1;i<=n;++i) fa[i]=i;
	int ans=1;
	for(re int i=1;i<=cnt;++i){
		sum=0;
		Dfs(i,a[i].l,0);
		ans=(ans*1ll*sum)%p;
		for(re int j=a[i].l;j<=a[i].r;++j){
			int u=getfa(e[j].u),v=getfa(e[j].v);
			if(u!=v) fa[u]=v;
		}
	}
	cout<<ans;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值