• 【JSOI2008】【BZOJ1016】最小生成树计数


    我就爱写矩阵树定理!!!
    就不写暴力!!!

    1016: [JSOI2008]最小生成树计数

    Time Limit: 1 Sec Memory Limit: 162 MB
    Submit: 3584 Solved: 1429
    [Submit][Status][Discuss]
    Description

    如今给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(假设两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。因为不同的最小生成树可能非常多,所以你仅仅须要输出方案数对31011的模就能够了。

    Input

    第一行包括两个数。n和m,当中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每一个节点用1~n的整数编号。

    接下来的m行,每行包括两个整数:a, b, c,表示节点a, b之间的边的权值为c,当中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有同样权值的边不会超过10条。

    Output

    输出不同的最小生成树有多少个。你仅仅须要输出数量对31011的模就能够了。

    Sample Input

    4 6

    1 2 1

    1 3 1

    1 4 1

    2 3 2

    2 4 1

    3 4 1
    Sample Output

    8
    HINT

    Source

    边权把全部边排序
    边权同样的边构成连通块
    每一个连通块做一遍矩阵树
    答案相乘

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define P 31011
    #define MAXN 110
    #define MAXINT 0x7fffffff
    using namespace std;
    int C[MAXN][MAXN],D[MAXN][MAXN],A[MAXN][MAXN];
    int block[MAXN][MAXN],top[MAXN];
    long long ans=1;
    int n,m;
    bool vis[MAXN];
    struct Set
    {
        int f[MAXN];
        int find(int x)
        {
            if (f[x]==x)    return x;
            return f[x]=find(f[x]);
        }
        void Union(int x,int y)
        {
            int a=find(x),b=find(y);
            f[b]=a;
        }
    }s1,s2;//对每一个连通块用s2,整体用s1 
    struct edge
    {
        int u,v,w;
        bool operator <(const edge& a)const
        {
            return w<a.w;
        }
    }e[MAXN*10];
    void in(int &x)
    {
        char ch=getchar();x=0;
        while (!(ch>='0'&&ch<='9')) ch=getchar();
        while (ch>='0'&&ch<='9')    x=x*10+ch-'0',ch=getchar();
    }
    int calc(int size)
    {
        int ret=1;
        for (int i=1;i<=size;i++)
            for (int j=1;j<=size;j++)   
                C[i][j]=(C[i][j]+P)%P;
        for (int i=1;i<=size;i++)
        {
            for (int j=i+1;j<=size;j++)
            {
                int a=C[i][i],b=C[j][i];
                while (b)
                {
                    int t=a/b;a%=b;swap(a,b);
                    for (int k=i;k<=size;k++)   C[i][k]=(C[i][k]-C[j][k]*t)%P;
                    for (int k=i;k<=size;k++)   swap(C[i][k],C[j][k]);
                    ret=-ret;
                }
            }
            if (!C[i][i])   return 0;
            ret*=C[i][i];ret%=P;
        }
        return (ret+P)%P;
    }
    void print()
    {
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++)  cout<<C[i][j]<<' ';
            cout<<endl;
        }   
    }
    void Print()
    {
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++)  cout<<A[i][j]<<' ';
            cout<<endl;
        }
        cout<<endl;
    }
    int main()
    {
        in(n);in(m);
        if (m<n-1)
        {
            cout<<0<<endl;
            return 0;
        }
        for (int i=1;i<=m;i++)  in(e[i].u),in(e[i].v),in(e[i].w);
        for (int i=1;i<=n;i++)  s1.f[i]=s2.f[i]=i;
        sort(e+1,e+m+1);
        int t=-MAXINT;
        for (int i=1;i<=m+1;i++)
        {
            if (e[i].w!=t||i>m)//边权不同,计算行列式值,进入下一个连通块
            {
                for (int j=1;j<=n;j++)
                    if (vis[j])
                    {
                        int F=s2.find(j);
                        block[F][++top[F]]=j;
                        vis[j]=0;
                    }
                for (int j=1;j<=n;j++)
                    if (top[j]>1)
                    {
                        memset(C,0,sizeof(C));
                        for (int k=1;k<=top[j];k++)//对连通块构建矩阵 
                            for (int l=k+1;l<=top[j];l++)
                            {
                                int a=block[j][k],b=block[j][l];
                                C[k][l]=(C[l][k]-=A[a][b]);
                                C[k][k]+=A[a][b];C[l][l]+=A[a][b];
                            }
                        ans=(ans%P*calc(top[j]-1))%P;
                        for (int k=1;k<=top[j];k++) s1.f[block[j][k]]=j;
                    }
                for (int j=1;j<=n;j++)
                {
                    s1.f[j]=s2.f[j]=s1.find(j);
                    top[j]=0;memset(block[j],0,sizeof(block[j]));
                }
                if (i>m)    break;
                t=e[i].w;
            }
            int x=s1.find(e[i].u),y=s1.find(e[i].v);
            if (x==y)   continue;
            vis[x]=vis[y]=1;
            s2.Union(x,y);
            D[x][x]++;D[y][y]++;A[x][y]++;A[y][x]++;
        }
        cout<<ans<<endl;
    }
  • 相关阅读:
    HDOJ1003 MaxSum【逆推】
    HDOJ1698 Just a hook【线段树成段更新lazy标志】武科大ACM暑期集训队选拔赛4题
    HDOJ1102 Constructing Roads【最小生成树】武科大ACM暑期集训队选拔赛1题
    POJ2828 Buy Tickets【线段树,逆序遍历】
    HDOJ1215 ( 七夕节 )【居然还可以这么解~】
    HDOJ1089HDOJ1096【格式练习】
    HDOJ1233 ( 还是畅通工程 ) 【最小生成树,kruscal】
    HDOJ1035 ( Robot Motion ) 【递归】
    POJ3468 A Simple Problem with Integers【线段树 成段更新+求和 lazy标志】
    HDOJ1216 Assistance Required【打表】武科大ACM暑期集训队选拔赛7题
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7200022.html
Copyright © 2020-2023  润新知