[JSOI2008 Tree最小生成树计数]

[关键字]:数学 树结构

[题目大意]:求出给定的图的最小生成树的数量。

//==============================================================================================

[分析]:有一个定理:所有最小生成树的权值为ci的边的数量ni和所连接的点集S是一样。由此可以判断每种权值的边拿出ni个构成S的最小生成树的数量,因为同样的边的数量最多只有10所以搜索即可,然后利用乘法原理成在一起。注意判断无解情况。

[代码]:

View Code
#include<iostream>

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<algorithm>

using namespace std;



const int MAXM=1005;

const int MAXN=105;

const int MOD=31011;



struct node

{

       int x,y,d;

}e[MAXM];

int n,m,size,tot;

int sum[MAXM],st[MAXM],f[MAXN],f2[MAXN];

bool v[MAXN];

long long temp,ans=1;

bool cmp(node a,node b){return a.d<b.d;}



int GET(int k)

{

    if (f[k]==k) return k;

    f[k]=GET(f[k]);

    return f[k];

}



int GET2(int k)

{

    /*if (f2[k]==k) return k;

    f2[k]=GET(f2[k]);

    return f2[k];*/

    if (f2[k]==k) return k;

       else return GET2(f2[k]);

}



void Union(int x,int y)

{

     int xx=GET(x),yy=GET(y);

     if (xx!=yy) f[xx]=yy;

}



void DFS(int st,int ed,int sum)

{

     if (sum==0) {++temp;return;}

     if (st>ed) return;

     int fa=GET2(e[st].x),fb=GET2(e[st].y);

     if (v[e[st].x] && v[e[st].y] && fa!=fb)

     {

                    f2[fa]=fb;

                    DFS(st+1,ed,sum-1);

                    f2[fa]=fa;

     }

     DFS(st+1,ed,sum);

}



void Work(int x)

{

     temp=0;

     DFS(st[x],st[x+1]-1,sum[x]);

     ans=(ans*temp)%MOD;

}



bool Kurscal()

{

     int i,j;

     sort(e+1,e+m+1,cmp);

     for (int i=1;i<=n;++i) f[i]=f2[i]=i;

     tot=size=0;

     memset(v,0,sizeof(v));

     for (i=1;i<=m;++i)

     {

         if (i==1 || e[i].d!=e[i-1].d)

         {

                  st[++tot]=i,sum[tot]=0;

                  if (i!=1) Work(tot-1);

                  for (j=1;j<=n;++j) f2[j]=f[j];

         }

         if (GET(e[i].x)!=GET(e[i].y))

         {

                                      ++size;

                                      Union(e[i].x,e[i].y);

                                      v[e[i].x]=v[e[i].y]=1;

                                      ++sum[tot];

         }

         if (size==n-1) break;

     }

     for (j=i+1;j<=m+1;++j)

         if (e[j].d!=e[j-1].d)

            {st[tot+1]=j;break;}

     Work(tot);

     if (size==n-1) return 1; else return 0;

}



int main()

{

    scanf("%d%d",&n,&m);

    for (int i=1;i<=m;++i)

        scanf("%d %d %d",&e[i].x,&e[i].y,&e[i].d);

    if (Kurscal()) printf("%I64d\n",ans); else printf("0\n");

    return 0;

}

你可能感兴趣的:(最小生成树)