最小生成树计数

Posted fangbozhen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小生成树计数相关的知识,希望对你有一定的参考价值。

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;
}

 

以上是关于最小生成树计数的主要内容,如果未能解决你的问题,请参考以下文章

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

BZOJ1016: [JSOI2008]最小生成树计数 深搜+并查集

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

最小生成树计数

BZOJ1016 最小生成树计数

最小生成树计数