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


    Time Limit: 1 Sec Memory Limit: 162 MB
    Submit: 7134 Solved: 2931
    [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,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

    解题思路

    matrix tree定理,就是一张图中生成树的个数为此图的度数矩阵-邻接矩阵得到的矩阵
    去掉一行一列之后的行列式的值。
    模拟最小生成树 克鲁斯卡尔算法,对于相等的边我们只需要考虑每条边对于答案的贡献,也就是他可以与哪些边相连,然后可以看成一堆权值相等的边相连的生成树个数。
    行列式用高斯消元可求,代码较为复杂。

    代码

    #include<bits/stdc++.h>
    #define LL long long
    
    using namespace std;
    const int MAXN = 1005;
    const double eps = 1e-7;
    const int mod = 31011;
    
    inline int rd(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
        return x*f;
    }
    
    int n,m,gg,is[MAXN];
    int head[MAXN],cnt,wtf;
    int fa[MAXN],sum[MAXN]; 
    int du[MAXN][MAXN];
    int a[MAXN][MAXN];
    double ans[MAXN][MAXN];
    bool used[MAXN];
    LL ANS=1;
    
    struct Edge{
        int fr,to,v;
    }edge[MAXN<<1];
    
    inline void add(int bg,int ed,int w){
        edge[++cnt].to=ed;
        edge[cnt].fr=bg;
        edge[cnt].v=w;
    }
    
    inline bool cmp(Edge A,Edge B){
        return A.v<B.v;
    }
    
    inline int find(int x){
        if(x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    
    inline LL gauss(int n){
        double K,s=1.0;
        for(register int i=1;i<=n;i++){
            if(fabs(ans[i][i])<eps){
                LL hv=0;
                for(register int j=i+1;j<=n;j++) 
                    if(fabs(ans[j][i])>=eps){
                        hv=1;
                        s=-s;
                        for(register int k=1;k<=n;k++)
                            swap(ans[i][k],ans[j][k]);
                        break;
                    }
                if(!hv) return 0;
            }
            K=1/ans[i][i];
            s=s*ans[i][i];
            for(register int j=i;j<=n;j++) ans[i][j]=ans[i][j]*K;
            for(register int j=i+1;j<=n;j++){
                K=ans[j][i];
                for(register int k=1;k<=n;k++)
                    ans[j][k]-=ans[i][k]*K;
            }
        }
    //  cout<<s<<endl;
        LL ret=(LL)(s+eps+eps);
        return ret;
    }
    
    int main(){
        n=rd();m=rd();
        for(register int i=1;i<=m;i++){
            int x,y,w;
            x=rd();y=rd();w=rd();
            add(x,y,w);
        }
        sort(edge+1,edge+1+cnt,cmp);
        for(register int i=1;i<=n;i++) fa[i]=i;
        gg=n;
        for(register int i=1;i<=m && gg>1;i++){
            int u=edge[i].fr;
            int v=edge[i].to;
            if(find(u)!=find(v)){
                used[i]=1;
                fa[find(u)]=find(v);
                gg--;
            }
        }
        if(gg>1) {
            puts("0");
            return 0;
        }
        LL u=1,v=1;
        while(v<=m){
            for(register int i=1;i<=n;++i){
                fa[i]=i;
                is[i]=0;
            }
            for(register int i=1;i<=m;i++){
                if(used[i] && edge[i].v!=edge[u].v)
                    fa[find(edge[i].fr)]=find(edge[i].to);
            }
            gg=0;
            for(register int i=1;i<=n;i++){
                if(!is[find(i)])
                    is[find(i)]=++gg;
                is[i]=is[find(i)];
            }
            for(register int i=1;i<=gg;i++)
                for(register int j=1;j<=gg;j++)
                    ans[i][j]=du[i][j]=a[i][j]=0;
            while(edge[u].v==edge[v].v){
                du[is[edge[v].fr]][is[edge[v].fr]]++;
                du[is[edge[v].to]][is[edge[v].to]]++;
                a[is[edge[v].fr]][is[edge[v].to]]++;
                a[is[edge[v].to]][is[edge[v].fr]]++;
                v++;
            }
            for(register int i=1;i<=gg;i++)
                for(register int j=1;j<=gg;j++)
                    ans[i][j]=du[i][j]-a[i][j];
    //      for(register int i=1;i<=n;i++){
    //          for(register int j=1;j<=n;j++)
    //              cout<<ans[i][j]<<" ";
    //          cout<<endl;
    //      }
            --gg;
            ANS=ANS*(gauss(gg))%mod;
    //      cout<<ANS<<endl;
            u=v;
        }
        printf("%lld",ANS);
        return 0;
    }
  • 相关阅读:
    python全栈学习--day57(响应式页面-@media介绍,移动端单位介绍,Bootstrap学习)
    网络编程-Socket介绍
    网络编程-五层协议详解
    python-封装
    python- 类的多态与多态性
    python-接口类与抽象类
    python-类的组合和使用
    python-类继承与重用
    python-面向对象编程小结
    python-属性的查找与绑定方法
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9677001.html
Copyright © 2020-2023  润新知