zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    白话SSL协议的握手过程
    PHP进程高负载
    Apache不定时宕机
    KindEditor
    过滤进程命令
    LAMP 平台必建安装包
    scp command
    ssl_request_log日志拆分
    Day01:Python入门
    Day03:集合、文件处理和函数基础
  • 原文地址:https://www.cnblogs.com/Zinn/p/9252400.html
Copyright © 2011-2022 走看看