码迷,mamicode.com
首页 > 其他好文 > 详细

最小生成树计数

时间:2019-11-04 22:03:43      阅读:103      评论:0      收藏:0      [点我收藏+]

标签:分离   void   for   sum   https   a+b   实现   href   连通性   

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\n",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;
}

最小生成树计数

标签:分离   void   for   sum   https   a+b   实现   href   连通性   

原文地址:https://www.cnblogs.com/fangbozhen/p/11794915.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!