zoukankan      html  css  js  c++  java
  • 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数

    题意:求一个图的最小生成树个数。

    矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值。

    度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是0。

    邻接矩阵G[i][j]的值为i与j之间的边数(重边要记入)。

    一个定理:一个图的所有MST中,相同权值的边数肯定是相等的。

    于是将边从小到大排序之后,根据权值划分阶段,将之前的点全缩点,这一阶段的边中仅考虑当前权值的边,然后把图划分成多个连通块,对每个连通块使用矩阵树定理求生成树个数,该阶段的值就是所有连通块乘起来。然后最终答案就是所有阶段的值乘起来。

    缩点使用了动态维护并查集的方法。

    无法求出生成树的情况要特判。

    md另外这题模的数是合数,所以高斯消元贼恶心……用了特殊的处理方法

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef vector<int>::iterator ITER;
    vector<int>bel[105];
    int v[2005],first[105],next[2005],w[2005],e;
    void AddEdge(int U,int V,int W){
    	v[++e]=V;
    	w[e]=W;
    	next[e]=first[U];
    	first[U]=e;
    }
    int fa[105];
    int find(int x){
    	return x==fa[x] ? x : fa[x]=find(fa[x]);
    }
    const int MOD=31011;
    struct Edge{
    	int u,v,w;
    	Edge(const int &u,const int &v,const int &w){
    		this->u=u;
    		this->v=v;
    		this->w=w;
    	}
    	Edge(){}
    	void read(){
    		scanf("%d%d%d",&u,&v,&w);
    	}
    }es[1005];
    bool cmp(const Edge &a,const Edge &b){
    	return a.w<b.w;
    }
    int n,m;
    bool vis[105];
    int num[105],p,now,A[105][105],ans=1;
    void dfs(int U){
    	num[U]=++p;
    	vis[U]=1;
    	for(ITER it=bel[U].begin();it!=bel[U].end();++it){
    		for(int i=first[*it];i;i=next[i]){
    			int V;
    			if(w[i]==now && (V=find(v[i]))!=U){
    //				printf("%d %d
    ",U,V);
    				if(!vis[V]){
    					dfs(V);
    				}
    				++A[num[V]][num[V]];
    				A[num[U]][num[V]]=(A[num[U]][num[V]]-1+MOD)%MOD;
    			}
    		}
    	}
    }
    int N;
    int guass_jordan()
    {
        int res=1;
        for(int i=1;i<=N;i++){
            for (int j=i+1;j<=N;j++){
                int a=A[i][i],b=A[j][i];
                while(b)
                {
                    int t=a/b;
    				a%=b;
    				swap(a,b);
                    for(int k=i;k<=N;k++){
                    	A[i][k]=(A[i][k]-A[j][k]*t%MOD+MOD)%MOD;
    				}
                    for(int k=i;k<=N;k++){
                    	swap(A[i][k],A[j][k]);
                    }
                    res=res*(MOD-1)%MOD;
                }
            }
            if(!A[i][i]){
            	return 0;
    		}
            res=(res*A[i][i])%MOD;
        }
        return res;
    }
    int tot;
    int main(){
    //	freopen("bzoj1016.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i){
    		fa[i]=i;
    		bel[i].push_back(i);
    	}
    	for(int i=1;i<=m;++i){
    		es[i].read();
    		AddEdge(es[i].u,es[i].v,es[i].w);
    		AddEdge(es[i].v,es[i].u,es[i].w);
    	}
    	sort(es+1,es+m+1,cmp);
    	for(int i=1;i<=m;++i){
    		if(es[i].w!=es[i-1].w){
    			now=es[i].w;
    			memset(vis,0,sizeof(vis));
    			for(int j=1;j<=n;++j){
    				if(j==fa[j] && !vis[j]){
    					p=0;
    					memset(num,0,sizeof(num));
    					dfs(j);
    					N=p-1;
    					ans=ans*guass_jordan()%MOD;
    					for(int k=1;k<=p;++k){
    						for(int l=1;l<=p;++l){
    							A[k][l]=0;
    						}
    					}
    				}
    			}
    		}
    		int f1=find(es[i].u),f2=find(es[i].v);
    		if(f1!=f2){
    			fa[f1]=f2;
    			++tot;
    			for(ITER it=bel[f1].begin();it!=bel[f1].end();++it){
    				bel[f2].push_back(*it);
    			}
    			bel[f1].clear();
    		}
    	}
    	printf("%d
    ",tot==n-1 ? ans : 0);
    	return 0;
    }
  • 相关阅读:
    antd pro2.0 使用记录五:设置代理
    antd pro2.0 使用记录四:右侧顶部菜单栏+新建页面
    antd pro2.0 使用记录三:多个接口调用问题
    antd pro2.0 记录二:登录/注册页面逻辑,调用后台
    实用的Portraiture滤镜磨皮教程
    mysql 远程访问权限
    vc6.0运用mysql数据库中的编码所导致的乱码问题(接收和输出的编码必须要一致)
    飞思卡尔imx6开发板Linux下GPIO驱动
    beagleBone black 中QT的移植
    Beaglebone Black从零开始系列教程大汇总!
  • 原文地址:https://www.cnblogs.com/autsky-jadek/p/7630373.html
Copyright © 2011-2022 走看看