zoukankan      html  css  js  c++  java
  • P4208 [JSOI2008]最小生成树计数

    P4208 [JSOI2008]最小生成树计数

    矩阵树定理+最小生成树

    神犇的题解

    ↑↑需要的2个定理

    根据定理,我们需要求出的是每层相同权值生成树方案之积

    所以在最小生成树求解过程中嵌入计算过程:每次建一个新图,计算新图行列式的值。

    因为模数不是质数所以高斯消元就用辗转相除了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    template <typename T> inline void read(T &x){
        char c=getchar(); x=0;
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    }
    const int mod=31011;
    struct edge{int from,to,dis;}a[1002],c[1002];
    inline bool cmp(const edge &A,const edge &B) {return A.dis<B.dis;}
    int n,m,ans=1,k,fa1[102],fa2[102],f[102][102],b[102]; bool vis[102];
    inline int find1(int x){return fa1[x]==x ? x:fa1[x]=find1(fa1[x]);}
    inline int find2(int x){return fa2[x]==x ? x:fa2[x]=find2(fa2[x]);}
    inline int det(int t){
        int res=1;
        for(int i=1;i<t;++i){
            for(int j=i+1;j<t;++j){
                while(f[j][i]){ //辗转相除
                    int div=f[i][i]/f[j][i];
                    for(int k=i;k<t;++k) f[i][k]=(f[i][k]-1LL*f[j][k]*div%mod+mod)%mod;
                    swap(f[i],f[j]); res=-res;
                }
            }
            res=(res+mod)%mod*f[i][i]%mod;
        }return (res+mod)%mod;
    }
    inline void mt(int st,int ed){
        memset(vis,0,sizeof(vis));
        memset(f,0,sizeof(f));
        for(int i=st;i<=ed;++i){
            c[i]=((edge){find1(a[i].from),find1(a[i].to),a[i].dis}); //新边,编号为原连通块的编号
            if(c[i].from==c[i].to) continue;
            if(!vis[c[i].from]) vis[c[i].from]=1;
            if(!vis[c[i].to]) vis[c[i].to]=1;
        }int cnt=0;
        for(int i=1;i<=n;++i) if(vis[i]) b[++cnt]=i,fa2[cnt]=cnt; //新点
        for(int i=st;i<=ed;++i){
            if(c[i].from==c[i].to) continue;
            int r1=find1(c[i].from),r2=find1(c[i].to);
            if(r1!=r2) fa1[r1]=r2,++k;
            int u=lower_bound(b+1,b+cnt+1,c[i].from)-b;
            int v=lower_bound(b+1,b+cnt+1,c[i].to)-b;
            ++f[u][u];++f[v][v];
            f[u][v]=(f[u][v]-1+mod)%mod; //每次减法取模
            f[v][u]=(f[v][u]-1+mod)%mod;
            r1=find2(u),r2=find2(v);
            if(r1!=r2) fa2[r1]=r2;
        }
        for(int i=1;i<cnt;++i){ //为了防止新图不连通,在每个连通块间加入一条唯一任意边。这并不会影响生成树方案数
            int r1=find2(i),r2=find2(i+1);
            if(r1==r2) continue;
            fa2[r1]=r2; ++f[r1][r1];++f[r2][r2];
            f[r1][r2]=(f[r1][r2]-1+mod)%mod;
            f[r2][r1]=(f[r2][r1]-1+mod)%mod;
        }ans=1LL*ans*det(cnt)%mod;
    }
    int main(){
        read(n); read(m);
        for(int i=1;i<=n;++i) fa1[i]=i;
        for(int i=1;i<=m;++i) read(a[i].from),read(a[i].to),read(a[i].dis);
        sort(a+1,a+m+1,cmp);
        for(int i=1,j;i<=m&&k<n-1;i=j){
            for(j=i+1;j<=m;++j) if(a[i].dis!=a[j].dis) break;
            if(j-i>1) {mt(i,j-1); continue;} //相同权值的边超过1条就要计算生成树的方案了
            int r1=find1(a[i].from),r2=find1(a[i].to);
            if(r1!=r2) fa1[r1]=r2,++k;
        }if(k<n-1) ans=0;
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    从搜索引擎角度看SEO
    关键词排名与网站优化有哪三大误区?
    真正提升关键词排名的外链应该怎样发?
    高质量外链的十大特性
    四个方面分析SEO如何提高网站的权重
    Linux(ubuntu)使用dd从iso制作win7安装u盘(读卡器一样),以及备份分区
    折腾slidingmenu
    生命游戏介绍
    21232f297a57a5a743894a0e4a801fc3
    final关键字
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9671240.html
Copyright © 2011-2022 走看看