列表

详情


NC20165. [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的模就可以了。

示例1

输入:

4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1

输出:

8

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(g++ 7.5.0) 解法, 执行用时: 2ms, 内存消耗: 348K, 提交时间: 2023-02-07 09:50:59

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mod=31011;
const int mac=1e3+10;

struct node
{
    int u,v,w;
    bool operator <(const node &a)const{
        return w<a.w;
    }
}eg[mac<<1];

int father[mac],l[mac],r[mac],c[mac],cnt,sum;

int find(int x){return x==father[x]?x:find(father[x]);}
//dfs(l[i],i,0)
void dfs(int now,int x,int num)
{
    if (now>r[x]){//从l[i]搜索到r[i],判断第i个长度的边是否用了c[cnt]个
        sum+=(num==c[x]);
        return;
    }
    int fa=find(eg[now].u),fb=find(eg[now].v);
    if (fa!=fb){
        father[fa]=fb;
        dfs(now+1,x,num+1);
        father[fa]=fa;father[fb]=fb;
    }
    dfs(now+1,x,num);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int n,m;
    scanf ("%d%d",&n,&m);
    for (int i=1; i<=m; i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        eg[i]=node{u,v,w};
    }
    sort(eg+1,eg+1+m);
    for (int i=1; i<=n; i++) father[i]=i;
    int tot=0;
    for (int i=1; i<=m; i++){
        if (eg[i].w!=eg[i-1].w) r[cnt]=i-1,l[++cnt]=i;
        int fa=find(eg[i].u),fb=find(eg[i].v);
        if (fa!=fb) ++c[cnt],father[fa]=fb,++tot;//c[cnt]用了第cnt边长度几次
    }
    r[cnt]=m;
    if (tot!=n-1) {printf("0\n");return 0;}
    for (int i=1; i<=n; i++) father[i]=i;
    int ans=1;
    for (int i=1; i<=cnt; i++){
        sum=0;
        dfs(l[i],i,0);
        ans=ans*sum%mod;
        for (int j=l[i]; j<=r[i]; j++){
            int fa=find(eg[j].u),fb=find(eg[j].v);
            if (fa!=fb) father[fa]=fb;
        }
    } 
    printf("%d\n",ans);
    return 0;
}

C++(clang++ 11.0.1) 解法, 执行用时: 3ms, 内存消耗: 468K, 提交时间: 2022-07-28 19:52:49

#include<bits/stdc++.h>
using namespace std;
struct edge{
	int a,b,c;
}e[1001];
int n,f[101],l[1002],c[1001],cnt,tot;
bool cmp(const edge &x,const edge &y){
	return x.c < y.c;
}
void init(){
	for (int i = 1;i <= n;i++)
		f[i] = i;
}
int find(int x){
	while (f[x] != x)
		x = f[x];
	return x;
}
void dfs(int now,int s,const int &x){
	if (s == c[x]){
		tot++;
		return;
	}
	int a,b;
	for (;now < l[x + 1];now++){
		a = find(e[now].a);
		b = find(e[now].b);
		if (a != b){
			f[a] = b;
			dfs(now + 1,s + 1,x);
			f[a] = a;
		}
	}
}
int main(){
	int m,x,y,i,j;
	cin>>n>>m;
	for (i = 1;i <= m;i++)
		cin>>e[i].a>>e[i].b>>e[i].c;
	sort(e + 1,e + 1 + m,cmp);
	init();
	for (i = 1;i <= m;i++){
		if (e[i].c != e[i - 1].c)
			l[++cnt] = i;
		x = find(e[i].a);
		y = find(e[i].b);
		if (x != y){
			f[x] = y;
			tot++;
			c[cnt]++;
		}
	}
	if (tot != n - 1){
		cout<<0;
		return 0;
	}
	l[cnt + 1] = m + 1;
	int ans = 1;
	init();
	for (i = 1;i <= cnt;i++){
		if (!c[i])continue;
		tot = 0;
		dfs(l[i],0,i);
		ans = ans * tot % 31011;
		for (j = l[i];j < l[i + 1];j++)
			f[find(e[j].a)] = find(e[j].b);
	}
	cout<<ans;
	return 0;
}

上一题