• 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);
    }
    
  • 相关阅读:
    线程、协程、Goroutine的区别和联系
    Docker是如何实现跨平台的等问题
    Docker容器与虚拟机有什么区别?
    带www和不带www的域名有什么区别
    netty 入门前阅读
    netty 入门示例
    thingsboard 入门教程
    RSAUtil 工具类
    java zip 打包
    redis 详细讲解
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13616710.html
Copyright © 2020-2023  润新知