• 最小生成树计数


    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;
    }
  • 相关阅读:
    在Win8下无法打开 hlp 帮助文件的问题
    ftp 终端命令
    为 Macbook 增加锁屏热键技巧
    苹果系统直接读写 ntfs 磁盘
    div 绝对布局居中
    2015-1-11
    unable to load default svn client
    ubuntu eclipse 安装svn
    centos mysq table is read only
    centos ssh 乱码
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11655165.html
Copyright © 2020-2023  润新知