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

Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 7134 Solved: 2931
[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,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过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

解题思路

matrix tree定理,就是一张图中生成树的个数为此图的度数矩阵-邻接矩阵得到的矩阵
去掉一行一列之后的行列式的值。
模拟最小生成树 克鲁斯卡尔算法,对于相等的边我们只需要考虑每条边对于答案的贡献,也就是他可以与哪些边相连,然后可以看成一堆权值相等的边相连的生成树个数。
行列式用高斯消元可求,代码较为复杂。

代码

#include<bits/stdc++.h>
#define LL long long

using namespace std;
const int MAXN = 1005;
const double eps = 1e-7;
const int mod = 31011;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
    return x*f;
}

int n,m,gg,is[MAXN];
int head[MAXN],cnt,wtf;
int fa[MAXN],sum[MAXN]; 
int du[MAXN][MAXN];
int a[MAXN][MAXN];
double ans[MAXN][MAXN];
bool used[MAXN];
LL ANS=1;

struct Edge{
    int fr,to,v;
}edge[MAXN<<1];

inline void add(int bg,int ed,int w){
    edge[++cnt].to=ed;
    edge[cnt].fr=bg;
    edge[cnt].v=w;
}

inline bool cmp(Edge A,Edge B){
    return A.v<B.v;
}

inline int find(int x){
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}

inline LL gauss(int n){
    double K,s=1.0;
    for(register int i=1;i<=n;i++){
        if(fabs(ans[i][i])<eps){
            LL hv=0;
            for(register int j=i+1;j<=n;j++) 
                if(fabs(ans[j][i])>=eps){
                    hv=1;
                    s=-s;
                    for(register int k=1;k<=n;k++)
                        swap(ans[i][k],ans[j][k]);
                    break;
                }
            if(!hv) return 0;
        }
        K=1/ans[i][i];
        s=s*ans[i][i];
        for(register int j=i;j<=n;j++) ans[i][j]=ans[i][j]*K;
        for(register int j=i+1;j<=n;j++){
            K=ans[j][i];
            for(register int k=1;k<=n;k++)
                ans[j][k]-=ans[i][k]*K;
        }
    }
//  cout<<s<<endl;
    LL ret=(LL)(s+eps+eps);
    return ret;
}

int main(){
    n=rd();m=rd();
    for(register int i=1;i<=m;i++){
        int x,y,w;
        x=rd();y=rd();w=rd();
        add(x,y,w);
    }
    sort(edge+1,edge+1+cnt,cmp);
    for(register int i=1;i<=n;i++) fa[i]=i;
    gg=n;
    for(register int i=1;i<=m && gg>1;i++){
        int u=edge[i].fr;
        int v=edge[i].to;
        if(find(u)!=find(v)){
            used[i]=1;
            fa[find(u)]=find(v);
            gg--;
        }
    }
    if(gg>1) {
        puts("0");
        return 0;
    }
    LL u=1,v=1;
    while(v<=m){
        for(register int i=1;i<=n;++i){
            fa[i]=i;
            is[i]=0;
        }
        for(register int i=1;i<=m;i++){
            if(used[i] && edge[i].v!=edge[u].v)
                fa[find(edge[i].fr)]=find(edge[i].to);
        }
        gg=0;
        for(register int i=1;i<=n;i++){
            if(!is[find(i)])
                is[find(i)]=++gg;
            is[i]=is[find(i)];
        }
        for(register int i=1;i<=gg;i++)
            for(register int j=1;j<=gg;j++)
                ans[i][j]=du[i][j]=a[i][j]=0;
        while(edge[u].v==edge[v].v){
            du[is[edge[v].fr]][is[edge[v].fr]]++;
            du[is[edge[v].to]][is[edge[v].to]]++;
            a[is[edge[v].fr]][is[edge[v].to]]++;
            a[is[edge[v].to]][is[edge[v].fr]]++;
            v++;
        }
        for(register int i=1;i<=gg;i++)
            for(register int j=1;j<=gg;j++)
                ans[i][j]=du[i][j]-a[i][j];
//      for(register int i=1;i<=n;i++){
//          for(register int j=1;j<=n;j++)
//              cout<<ans[i][j]<<" ";
//          cout<<endl;
//      }
        --gg;
        ANS=ANS*(gauss(gg))%mod;
//      cout<<ANS<<endl;
        u=v;
    }
    printf("%lld",ANS);
    return 0;
}
posted @ 2018-07-01 21:51  Monster_Qi  阅读(...)  评论(... 编辑 收藏