bzoj1016 [JSOI2008]最小生成树计数——Kruskal+矩阵树定理

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1016

从 Kruskal 算法的过程来考虑产生多种方案的原因,就是边权相同的边有一样的功能,从而带来了多种选择;

对于每一层次(边权相同)的边来说,它们最终会把图进一步连通;

所以在这一层之前缩好点,看看这一层连接出几个新连通块,对于每个连通块内部做矩阵树定理求生成树个数,再乘法原理乘起来即可;

注意高斯消元的矩阵不能直接用原图的点标号等,求行列式会出错;

疑惑:以及高斯消元 return 时为什么要加个 abs?

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
vector<int>v[105];
int n,m,fa[105],pa[105],a[105][105],c[105][105],ans=1,mod=31011;
bool vis[105];
struct N{
    int hd,to,w;
    N(int h=0,int t=0,int w=0):hd(h),to(t),w(w) {}
}edge[1005];
bool cmp(N x,N y){return x.w<y.w;}
int find(int x,int f[105]){return f[x]==x?x:find(f[x],f);}//
int gauss(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            a[i][j]%=mod;//
    int fl=0,ret=1;
    for(int i=1;i<=n;i++)
    {
        int t=i;
        for(int j=i+1;j<=n;j++)
            if(abs(a[j][i])>abs(a[t][i]))t=j;//abs
        if(t!=i)
        {
            fl^=1;
            for(int j=i;j<=n;j++)swap(a[i][j],a[t][j]);
        }
        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++)
                {
                    int tp=a[i][k]; a[i][k]=a[j][k];//a=b
                    a[j][k]=(tp-a[j][k]*tmp)%mod;//b=a%b
                }
                fl^=1;
            }
        (ret*=a[i][i])%=mod;
    }
    return (abs(ret)%mod+mod)%mod;//abs!?
//  return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1,x,y,z;i<=m;i++)
        scanf("%d%d%d",&edge[i].hd,&edge[i].to,&edge[i].w);
    sort(edge+1,edge+m+1,cmp);
    for(int i=1;i<=m+1;i++)
    {
        if(edge[i].w!=edge[i-1].w || i==m+1)
        {
            for(int j=1;j<=n;j++)
            {
                if(!vis[j])continue;
                int f1=find(j,pa);
                v[f1].push_back(j);//v是点的集合 
                vis[j]=0;
            }
            for(int j=1;j<=n;j++)
            {
                if(v[j].size()<=1)continue; 
                memset(a,0,sizeof a);
                int siz=v[j].size();
                for(int k=0;k<siz;k++)
                    for(int l=k+1;l<siz;l++)
                    {
                        int x=v[j][k],y=v[j][l],t=c[x][y];
//                      a[x][x]+=t; a[y][y]+=t;
//                      a[x][y]-=t; a[y][x]-=t;
                        a[k+1][k+1]+=t; a[l+1][l+1]+=t;
                        a[k+1][l+1]-=t; a[l+1][k+1]-=t;//
                    }
                (ans*=gauss(siz-1))%=mod;
//                (ans*=gauss(n-1))%=mod;
                for(int k=0;k<siz;k++)fa[v[j][k]]=j;
            }
            for(int j=1;j<=n;j++)
            {
                pa[j]=fa[j]=find(j,fa);
                v[j].clear();
            }
        }
        int f1=find(edge[i].hd,fa),f2=find(edge[i].to,fa);
        if(f1==f2)continue;
        pa[find(f1,pa)]=find(f2,pa); vis[f1]=1; vis[f2]=1;
        c[f1][f2]++; c[f2][f1]++;
    }
    for(int i=2;i<=n;i++)//!
        if(find(i,fa)!=find(i-1,fa)){printf("0"); return 0;}
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-07-02 09:38  Zinn  阅读(195)  评论(0编辑  收藏  举报