zoukankan      html  css  js  c++  java
  • BZOJ 1016: [JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数

    Time Limit: 1 Sec  Memory Limit: 162 MB

    Submit: 6357  Solved: 2575

    [Submit][Status][Discuss]

    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,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过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

    题解

    性质:一个无向图所有的最小生成树中某种权值的边的数目均相同。

    证明:(by wyfcyx_forever

    开始时,每个点单独构成一个集合。

    首先只考虑权值最小的边,将它们全部添加进图中,并去掉环,由于是全部尝试添加,那么只要是用这种权值的边能够连通的点,最终就一定能在一个集合中。

    那么不管添加的是哪些边,最终形成的集合数都是一定的,且集合的划分情况一定相同。那么真正添加的边数也是相同的。因为每添加一条边集合的数目便减少1.

    那么权值第二小的边呢?我们将之间得到的集合每个集合都缩为一个点,那么权值第二小的边就变成了当前权值最小的边,也有上述的结论。

    因此每个阶段,添加的边数都是相同的。我们以权值划分阶段,那么也就意味着某种权值的边的数目是完全相同的。

    先跑出一个最小生成树,map[i]记录权值为i的边的数目。

    从小到大处理每一种权值的边,状压枚举所有这种权值的边,看这种权值的边出现map[i]次时能否全部加入当前的森林。若能,则这种权值的边加入的方案数+1。

    最终答案是每种权值的边能加入的方案数的乘积。

    代码

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<iostream>
    #include<map>
    using namespace std;
    const int N=105,M=1005,C=1000000005,mod=31011;
    int n,m,tot,t,ans=1;
    int fa[N],temp[N];
    map<int,int>cnt;
    struct edge{
    	int u,v,w;
    }e[M];
    bool cmp(edge a,edge b){
    	return a.w<b.w;
    }
    int find(int u){
    	return fa[u]==u?u:fa[u]=find(fa[u]);
    }
    int count(int x){
    	int ret=0;
    	while(x){
    		if(x&1)ret++;
    		x>>=1;
    	}
    	return ret;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	int u,v,w;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d",&u,&v,&w);
    		e[i]=(edge){u,v,w};
    	}
    	sort(e+1,e+m+1,cmp);
    	for(int i=1;i<=n;i++)fa[i]=i;
    	int fu,fv;
    	for(int i=1;i<=m;i++){
    		u=e[i].u,v=e[i].v,w=e[i].w;
    		fu=find(u),fv=find(v);
    		if(fu!=fv){
    			fa[fu]=fv;
    			cnt[w]++;
    			tot++;
    		}
    		if(tot==n-1)break;
    	}
    	if(tot<n-1){
    		printf("0
    ");
    		return 0;
    	}
    	int l,r,fg;
    	for(int i=1;i<=n;i++)fa[i]=i;
    	for(l=1;l<=m;l=r+1){
    		memcpy(temp,fa,sizeof(temp));
    		t=0;
    		w=e[l].w;
    		r=l;
    		while(e[r+1].w==w)r++;
    		for(int i=0;i<(1<<(r-l+1));i++){
    			fg=1;
    			if(count(i)!=cnt[w])continue;
    			memcpy(fa,temp,sizeof(temp));
    			for(int j=l;j<=r;j++){
    				if((i>>(j-l))&1){
    					u=e[j].u,v=e[j].v;
    					fu=find(u),fv=find(v);
    					if(fu==fv){
    						fg=0;
    						break;
    					}
    					fa[fu]=fv;
    				}
    			}
    			if(fg)t++;
    		}
    		ans=(ans*t)%mod;
    		memcpy(fa,temp,sizeof(temp));
    		for(int i=l;i<=r;i++){
    			u=e[i].u,v=e[i].v;
    			fu=find(u),fv=find(v);
    			if(fu!=fv)fa[fu]=fv;
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
  • 相关阅读:
    next_permutation( ) 和prev_permutation( ) 全排列函数
    F
    STL入门
    H
    C
    提交按钮组件
    JScorllPane面板(带滚轮的JPane)
    JPanel画板
    网络布局管理器
    边界布局管理器
  • 原文地址:https://www.cnblogs.com/chezhongyang/p/7666205.html
Copyright © 2011-2022 走看看