BZOJ1016 最小生成树计数

Description

现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。

 

思路:考虑最小生成树的性质:1)每种边权的边数相同(若不相同,答案就会改变);2)从小到大连晚某种边权后,点的连通状态相同(借助kruskal过程理解)。鉴于题目中相同边权的边不超过10条,我们可以对于每种边权的边dfs一下,判断可行的解,累乘起来就可以了。

#include<iostream>

#include<cstdio>

#include<algorithm>

#define maxedge 1010

#define maxnode 110

#define P 31011

using namespace std;

struct use{

    int x1,x2,va,en;

}edge[maxedge*2]={0};

int fa[maxnode]={0},nefa[maxnode]={0},lafa[maxnode]={0},cc,n;

bool visit[maxedge*2]={false};

int my_comp(const use x,const use y)

{

    if (x.va<y.va) return 1;

    else return 0;

}

int rool(int x)

{

    if (fa[x]!=x) fa[x]=rool(fa[x]);

    return fa[x];

}

int nerool(int x)

{

    if (nefa[x]!=x) nefa[x]=nerool(nefa[x]);

    return nefa[x];

}

bool judge(int fi,int up)

{

    int i,j,r1,r2;

    for (i=1;i<=n;++i) nefa[i]=lafa[i];

    for (i=fi;i<=up;++i)

    {

        if (visit[i])

        {

            r1=nerool(edge[i].x1),r2=nerool(edge[i].x2);

            if (r1==r2) return false;

            if (r1<r2) nefa[r2]=r1;

            else nefa[r1]=r2;

        }

    }

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

    {

        nerool(i);rool(i);

        if (fa[i]!=nefa[i]) return false;

    }

    return true;

}

void dfs(int i,int tot,int la,int fi,int up)

{

    int j;

    if (i>tot)

    {

        if (judge(fi,up)) 

        {

          ++cc;if (cc>=P) cc-=P;

        }

        return;

    }

    for (j=la+1;j<=up;++j)

    {

        visit[j]=true;

        dfs(i+1,tot,j,fi,up);

        visit[j]=false;

    }

}

int main()

{

    int m,i,j,tot,r1,r2,ans;

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

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

        scanf("%d%d%d",&edge[i].x1,&edge[i].x2,&edge[i].va);

    sort(edge+1,edge+m+1,my_comp);

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

    {

        j=i;edge[i].en=i;

        while(edge[j].va==edge[j+1].va) ++j;

        edge[i].en=j;

    }

    tot=0;

    for (i=1;i<=n;++i) fa[i]=i;

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

    {

        r1=rool(edge[i].x1);r2=rool(edge[i].x2);

        if (r1!=r2)

        {

            if (r1<r2) fa[r2]=r1;

            else fa[r1]=r2;

            ++tot;

            if (tot==n-1) break;

        }

    }

    if (tot<n-1) printf("0\n");

    else

    {

        ans=1;

        for (i=1;i<=n;++i) fa[i]=i;

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

        {

            tot=0;

            for (j=1;j<=n;++j) {rool(j);lafa[j]=fa[j];}

            for (j=i;j<=edge[i].en;++j)

            {

                r1=rool(edge[j].x1);r2=rool(edge[j].x2);

                if (r1!=r2)

                {

                    if (r1<r2) fa[r2]=r1;

                    else fa[r1]=r2;

                    ++tot;

                }

            }

            cc=0;

            dfs(1,tot,i-1,i,edge[i].en);

            ans=(ans*cc)%P;

        }

        printf("%d\n",ans);

    }

}
View Code

从这道题目中,对最小生成树有了新的认识。

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