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

    题意

    给定一个 (n) 个点 (m) 条边的图,求最小生成树的个数。

    ( exttt{Data Range:}1leq nleq 100,1leq mleq 10^4)

    题解

    一道好题。

    根据本题后面提供的与那题正解没什么关联的方法可知,这个操作过程是这样的:

    首先求出原图的某一个最小生成树,接下来考虑从小到大枚举最小生成树上边的边权 (w)

    将最小生成树上边权不为 (w) 的边保留下来进行缩点,接下来再连上不在最小生成树中边权为 (w) 的边。

    这个时候会建出一个无向图,对每一个可能的 (w) 建出的图求一下生成树的个数乘起来即可。

    证明的话可以利用 Kruskal 的性质,求生成树的时候使用 Matrix-Tree 定理即可。

    模合数求行列式的方法是辗转相消,每一次需要交换两行,行列式要乘上 (-1),实在不理解可以看我代码

    容易看出这个东西的复杂度是 (O(n^3log n)) 的。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef int ll;
    typedef long long int li;
    const ll MAXN=251,MOD=31011;
    struct EdgeForKruskal{
        ll from,to,dist;
        inline bool operator <(const EdgeForKruskal &rhs)const
        {
            return this->dist<rhs.dist;
        }
    };
    EdgeForKruskal ed[2051],tree[MAXN];
    ll n,m,x,y,z,kk,res=1,totw; 
    ll ffa[MAXN],bel[MAXN],mat[MAXN][MAXN],wt[MAXN];
    inline ll read()
    {
        register ll num=0,neg=1;
        register char ch=getchar();
        while(!isdigit(ch)&&ch!='-')
        {
            ch=getchar();
        }
        if(ch=='-')
        {
            neg=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            num=(num<<3)+(num<<1)+(ch-'0');
            ch=getchar();
        }
        return num*neg;
    }
    inline void add(ll x,ll y)
    {
        mat[x][y]--,mat[y][x]--,mat[x][x]++,mat[y][y]++;
    }
    inline ll find(ll x)
    {
        return x==ffa[x]?x:ffa[x]=find(ffa[x]);
    }
    inline void setup(ll n)
    {
        for(register int i=1;i<=n;i++)
        {
            ffa[i]=i;
        }
    }
    inline void merge(ll x,ll y)
    {
        ll fx=find(x),fy=find(y);
        fx!=fy?ffa[fy]=fx:1;
    }
    inline ll Kruskal()
    {
        ll tott=0;
        for(register int i=1;i<=m;i++)
        {
            if(find(ed[i].from)!=find(ed[i].to))
            {
                merge(ed[i].from,ed[i].to),tree[++tott]=ed[i];
                if(wt[totw]!=ed[i].dist)
                {
                    wt[++totw]=ed[i].dist;
                }
                if(tott==n-1)
                {
                    break;
                }
            }
        }
        return tott==n-1;
    }
    inline void mergePoint(ll wt)
    {
        for(register int i=1;i<n;i++)
        {
            tree[i].dist!=wt?merge(tree[i].from,tree[i].to):(void)1;
        }
    }
    inline ll det(ll n)
    {
        ll res=1,sgn=1,cof;
        for(register int i=1;i<=n;i++)
        {
            for(register int j=1;j<=n;j++)
            {
                mat[i][j]=(mat[i][j]+MOD)%MOD;
            }
        }
        for(register int i=1;i<=n;i++)
        {
            for(register int j=i+1;j<=n;j++)
            {
                while(mat[j][i])
                {
                    cof=mat[i][i]/mat[j][i];
                    for(register int k=i;k<=n;k++)
                    {
                        mat[i][k]=(mat[i][k]-(li)cof*mat[j][k]%MOD+MOD)%MOD;
                    }
                    swap(mat[i],mat[j]),sgn*=-1;
                }
            }
        }
        for(register int i=1;i<=n;i++)
        {
            res=(li)res*mat[i][i]%MOD;
        }
        return sgn==1?res:MOD-res;
    }
    inline ll calc(ll wt)
    {
        ll blk=0;
        memset(mat,0,sizeof(mat)),setup(n),mergePoint(wt);
        for(register int i=1;i<=n;i++)
        {
            find(i)==i?bel[i]=++blk:1;
        }
        for(register int i=1;i<=n;i++)
        {
            bel[i]=bel[find(i)];
        }
        for(register int i=1;i<=m;i++)
        {
            ed[i].dist==wt?add(bel[ed[i].from],bel[ed[i].to]):(void)1;
        }
        return det(blk-1);
    }
    int main()
    {
        n=read(),m=read();
        for(register int i=1;i<=m;i++)
        {
            x=read(),y=read(),z=read(),ed[i]=(EdgeForKruskal){x,y,z};    
        }
        sort(ed+1,ed+m),setup(n);
        if(!Kruskal())
        {
            return puts("0"),0;
        }
        for(register int i=1;i<=totw;i++)
        {
            res=res*calc(wt[i])%MOD;
        }
        printf("%d
    ",res);
    }
    
  • 相关阅读:
    Java 蓝桥杯 算法训练 貌似化学
    Java 蓝桥杯 算法训练 貌似化学
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    Java 蓝桥杯 算法训练 字符串的展开 (JAVA语言实现)
    JAVA-蓝桥杯-算法训练-字符串变换
    Ceph:一个开源的 Linux PB 级分布式文件系统
    shell 脚本监控程序是否正在执行, 如果没有执行, 则自动启动该进程
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13616710.html
Copyright © 2011-2022 走看看