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

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
 
------------------------------------------我是娇羞的分割线----------------------------------------
求最小生成树的kruskal算法是个好东西。
在kruskal算法中,我们总是尽可能地把权值较小的边加入最小生成树,这样得到的最小生成树一定是最优的。
所以对于每一种权值的边,我们所选择的加入最小生成树的边的数量是确定的(如果多选择一条边,则这条边一定是多余的;如果少选择一条边,那么就会有一条权值更大的边来代替这条边,显然这样构造出来的就不是最小生成树了)。
因此,我们对每一种权值的边单独考虑。
注意到同一种权值的边最多有10条,满足搜索复杂度。
首先计算出这种权值的边我们需要选择多少条,然后dfs判断加入哪些边,统计方案数即可。
最后运用乘法原理计算出总方案数。
注意,这里的并查集不能用路径压缩!因为dfs回溯的时候需要把之前加入的边删去。
我的代码有几个地方可能写繁了,不过数据范围很和谐,8ms跑过丝毫不怂。
别忘了特判图本身不连通的情况!!!
/**************************************************************
    Problem: 1016
    User: xialan
    Language: C++
    Result: Accepted
    Time:8 ms
    Memory:1308 kb
****************************************************************/
 
#include<iostream>
#include<cstdio>
#include<climits>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
inline int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
const int M=1001,MOD=31011;
int fa[M],sum,j,MAX;
struct edge{int l,r,v;}e[M];
bool cmp(edge x,edge y){return x.v<y.v;}
int Find(int x){return fa[x]==x?x:Find(fa[x]);}
void dfs(int x,int s){
    if(x==j+1){
        if(s>MAX)MAX=s,sum=1;
        else if(s==MAX)sum++;
        return;
    }
    int fx=Find(e[x].l),fy=Find(e[x].r);
    if(fx!=fy){
        fa[fy]=fx;
        dfs(x+1,s+1);
        fa[fy]=fy;
    }
    dfs(x+1,s);
}
int main(){
    int n=read(),m=read();
    rep(i,1,n)fa[i]=i;
    rep(i,1,m){e[i].l=read();e[i].r=read();e[i].v=read();}
    sort(e+1,e+m+1,cmp);
    int ans=1,i=1;
    while(i<=m){
        j=i;
        while(j<m&&e[j+1].v==e[j].v)j++;
        MAX=-1;
        dfs(i,0);
        ans=ans*sum%MOD;
        rep(k,i,j){
            int fx=Find(e[k].l),fy=Find(e[k].r);
            if(fx!=fy)fa[fy]=fx;
        }           
        i=j+1;
    }
    bool flag=1;int f=Find(1);
    rep(i,2,n)if(Find(i)!=f){flag=0;break;}
    if(flag)printf("%d\n",ans);else printf("0\n");
    return 0;
}