BZOJ 1016 [JSOI2008]最小生成树计数 Kruskal Matrix-Tree定理

#include <cstdio>
#include <algorithm>
#define N 105
#define MOD 31011
using namespace std;
struct Edge {
    int u,v,c;
    Edge() {}
    void scan() { scanf("%d%d%d",&u,&v,&c); }
    bool operator < (const Edge& rhs) const {
        return c<rhs.c;
    }
}e[N*10];
int n,m,ans=1,rest,top,_top,pa[N],d[N],s[N],_s[N],f[N],a[N][N],b[N][N];
int root(int x) { return pa[x]==x ? pa[x] : root(pa[x]); }
int det() {
    int re=1;
    int _n=_top-1;
    for(int i=1;i<=_n;++i) {
        for(int j=i+1;j<=_n;++j)
            while(a[j][i]) {
                int tmp=a[i][i]/a[j][i];
                for(int k=i;k<=n;++k) a[i][k]-=a[j][k]*tmp;
                for(int k=i;k<=n;++k) swap(a[i][k],a[j][k]);
                re=-re;
            }
        if(!a[i][i]) return 0;
        re=re*a[i][i]%MOD;
    }
    return re;
}
inline bool cmp(const int& lhs,const int& rhs) { return pa[lhs]<pa[rhs]; }
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i) e[i].scan();
    for(int i=1;i<=n;++i) pa[i]=i;
    rest=n;
    sort(e+1,e+m+1);
    for(int l=1,r;l<=m && rest>1;l=r) {
        r=l;
        while(r<=m && e[l].c==e[r].c) ++r;
        for(int i=1;i<=n;++i) f[i]=d[i]=0;
        top=0;
        for(int i=1;i<=n;++i)
            if(pa[i]==i) s[++top]=i, f[i]=top;
        for(int i=1;i<=top;++i)
            for(int j=1;j<=top;++j)
                b[i][j]=0;
        for(int i=l;i<r;++i) {
            int pa_u=root(e[i].u),pa_v=root(e[i].v);
            if(pa_u==pa_v) continue;
            ++d[f[pa_u]], ++d[f[pa_v]];
            ++b[f[pa_u]][f[pa_v]], ++b[f[pa_v]][f[pa_u]];
        }
        for(int i=l;i<r;++i) {
            int pa_u=root(e[i].u),pa_v=root(e[i].v);
            if(pa_u==pa_v) continue;
            pa[pa_u]=pa_v;
            --rest;
        }
        for(int i=1;i<=top;++i) pa[s[i]]=root(s[i]);
        sort(s+1,s+top+1,cmp);
        for(int _l=1,_r;_l<=top;_l=_r) {
            _r=_l;
            while(_r<=top && pa[s[_r]]==pa[s[_l]]) ++_r;
            if(_l+1==_r) continue;
            _top=0;
            for(int i=_l;i<_r;++i) _s[++_top]=f[s[i]];
            for(int i=1;i<=_top;++i)
                for(int j=1;j<=_top;++j) {
                    a[i][j]=-b[_s[i]][_s[j]];
                    if(i==j) a[i][j]+=d[_s[i]];
                }
            ans=ans*det()%MOD;
        }
    }
    if(rest>1) ans=0;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值