zoukankan      html  css  js  c++  java
  • 最小生成树计数

    https://loj.ac/problem/10070

    题目描述

      给出一张图,求它最小生成树的个数。

    思路

      这道题不论是暴力还是矩阵树定理都需要一个定理:同一个图中的所有最小生成树的边权的数量都一定。

      证明:假设定理不成立,那我们必定可以有两条最小生成树边(a、b)和非树边(x、y),满足权值(a+b=x+y),并且删除(a、b)再加入(x、y)后仍是一棵最小生成树。我们不妨假设(v[x]<v[y])(v[a]<v[b]),那么删除两条边后会形成三个连通支,而由于(v[x]!=v[a])(v[y]!=v[b]),所以必定会有(x)连接(T1、T2)并且小于连接这两连通支的树边,或(a)连接(T1、T2)并且小于连接这两连通支的非树边。这两种情况都会使另一个最小生成树并不是最小,因为可以替换为更小的边。

      有了这个定理后,我们先考虑暴力。我们可以先做一次(Kruskal)求出最小生成树并统计每个权值边的数量,接下来我们对每一种边权(从小到大顺序),对于某种边权,我们暴力(dfs)枚举选和不选两种状态(因为同种边权数量较少),用并查集盘连通性,不过需要注意为了快速实现连通块的分离,我们不能使用路径压缩。乘法原理统计答案即可。

      对于同种边权较多的情况,我们选用矩阵树定理。(待我学习一下)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=110,MAXM=1010,mod=31011;
    struct Edge
    {
        int x,y,v;
    }e[MAXM];
    struct aa
    {
        int l,r,cnt;
    }a[MAXM];
    int fa[MAXN],sum;
    bool cmp(Edge x,Edge y)
    {
        return x.v<y.v;
    }
    int find(int x)
    {
        return fa[x]==x?x:find(fa[x]);
    } 
    void dfs(int u,int k,int x)
    {
    //    cout<<u<<' '<<k<<' '<<x<<' '<<a[x].r<<endl;
        if(u==a[x].r+1)
        {
            if(k==a[x].cnt)sum++;
            return ;
        }
        int fx=find(e[u].x),fy=find(e[u].y);
        if(fx!=fy)
        {
            fa[fx]=fy;
            dfs(u+1,k+1,x);
            fa[fx]=fx;fa[fy]=fy;
        }
        dfs(u+1,k,x);
    }
    int main() 
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
        for(int i=1;i<=n;i++)
            fa[i]=i;
        sort(e+1,e+m+1,cmp);
    //    for(int i=1;i<=m;i++)
    //        printf("%d %d
    ",i,e[i].v);
        int k=0,tot=0;
        for(int i=1;i<=m;i++)
        {
            if(e[i].v!=e[i-1].v)
            {
                a[++k].l=i;
                a[k-1].r=i-1;
            }
            int fx=find(e[i].x),fy=find(e[i].y);
            if(fx!=fy)
            {
                fa[fx]=fy;
                a[k].cnt++;
                tot++;
            }
        }
        if(tot!=n-1)
        {
            printf("0");
            return 0;
        }
        a[k].r=m;
        int ans=1;
        for(int i=1;i<=n;i++)
            fa[i]=i;
        for(int i=1;i<=k;i++)
        {
    //        cout<<i<<' '<<a[i].cnt<<' '<<a[i].l<<' '<<a[i].r<<endl;
            if(!a[i].cnt)continue ;
            sum=0;
    //        cout<<'x'<<i<<endl;
            dfs(a[i].l,0,i);
            ans=ans*sum%mod;
    //        cout<<sum<<endl;
            for(int j=a[i].l;j<=a[i].r;j++)
            {
                int x=e[j].x,y=e[j].y;
                int fx=find(x),fy=find(y);
                if(fx!=fy)fa[fx]=fy;
            }
        }
        printf("%d",ans);
        return 0;
    }
    
  • 相关阅读:
    hadoop再次集群搭建(3)-如何选择相应的hadoop版本
    48. Rotate Image
    352. Data Stream as Disjoint Interval
    163. Missing Ranges
    228. Summary Ranges
    147. Insertion Sort List
    324. Wiggle Sort II
    215. Kth Largest Element in an Array
    快速排序
    280. Wiggle Sort
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11794915.html
Copyright © 2011-2022 走看看