【BZOJ 1016】最小生成树计数(MST+搜索)

8 篇文章 0 订阅
3 篇文章 0 订阅

传送门

    最小生成树计数
    题意:求不同形态的最小生成树总数

I think

    为什么会有不同形态的最小生成树?前提是有了权值相同的边。
    我们可以证明,在不同的最小生成树中,权值相同的边的数量是相同的。首先,我们将每个点置于不同的集合中。取权值最小边集,去除环,合并剩余边的端点集合。无论去除环上的任意边,被该权值的边合并的端点集合数量不变,且形态一致。
    同理对于权值次小边集,次次小边集……
    于是问题转化为搜索每种权值的边集生成树的不同形态数,最后乘法原理解决。
    需要注意的是,在使用并查集时不能路径压缩,因为搜索的回溯中不便更改。

Code

#include<cstdio>
#include<algorithm>
typedef long long LL;
const int sm = 1e2+5;
const int sn = 1e3+5;
const int Mod = 31011;

int N,M,tot,Ct;
int Fa[sm];
LL Ans=1,ret;
struct Ed { 
    int u,v,w; 
    bool operator < (const Ed&a ) const {
        return w<a.w;
    }
}e[sn];
struct Data { int l,r,v; }a[sn];

void read(int &x) {
    char ch=getchar();x=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); 
}

int Find(int x) { return x==Fa[x]?x:Find(Fa[x]); }
void Dfs(int x,int cur,int k) {
    if(cur==a[x].r+1) {
        if(k==a[x].v) ++ret;
        return;
    }
    int p=Find(e[cur].u);
    int q=Find(e[cur].v);
    if(p!=q) {
        Fa[p]=q;
        Dfs(x,cur+1,k+1);
        Fa[p]=p,Fa[q]=q;
    }
    Dfs(x,cur+1,k);
}
int main() {
    int u,v;
    read(N),read(M);
    for(int i=1;i<=M;++i)
        read(e[i].u),read(e[i].v),read(e[i].w);

    std::sort(e+1,e+M+1);
    for(int i=1;i<=N;++i) Fa[i]=i;
    for(int i=1;i<=M;++i) {
        if(e[i].w!=e[i-1].w) a[Ct].r=i-1,a[++Ct].l=i;
        u=Find(e[i].u),v=Find(e[i].v);
        if(u!=v) Fa[u]=v,++a[Ct].v,++tot;
    }
    a[Ct].r=M;

    if(tot!=N-1) { puts("0");return 0; }

    for(int i=1;i<=N;++i) Fa[i]=i;
    for(int i=1;i<=Ct;++i) {
        if(a[i].v&&a[i].r-a[i].l+1!=a[i].v) {
            ret=0,Dfs(i,a[i].l,0);
            Ans=Ans*ret%Mod;
        }
        for(int j=a[i].l;j<=a[i].r;++j) {
            u=Find(e[j].u),v=Find(e[j].v);
            if(u!=v) Fa[u]=v;
        }
    }
    printf("%lld\n",Ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值