zzzyc

导航

[JSOI2008]最小生成树计数

这个题的思路主要有两种。。。本来博主以为自己的方法仅有自己胡。。。后来又翻到了有的大佬也这么写。。。

代码实现比较简单。。。确定做法正确之后就很快就可以搞定了。。。哈~哈~哈~

最小生成树满足两个性质:

1.它一定是一棵树它一定有 n-1 条边。。。

2.如果是最小的。。。那么优先选择权值小的边。。。

这个地方只要小意会一下就可以了。。。可能有点绕。。。

如果要保证这棵树的权值最小。。。

那么所用的某种权值的边的数量是一定的。。。

所以只要统计一下。。。

题目又很友好的给出了提示具有相同权值的边不会超过10条。。。

辣么我们只要对每种权值的边进行 dfs 判断它的方案数就可以了。。。

呆码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define mo 31011;
using namespace std;

int fa[1010],sum,n,m,cnt,tot;

struct asd{
    int x,y;
    int dis;
} a[1010];

struct sdf{
    int l,r,num;
} e[1010];

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

inline void unionn(int x,int y) { fa[x]=y; }

inline void unionu(int x,int y) { fa[x]=x; fa[y]=y; }

inline bool cmp(asd x,asd y)
{
    return x.dis<y.dis;
}

inline void dfs(int x,int now,int num)
{
    if(now==e[x].r+1)
    {
        if(num==e[x].num) sum++;
        return;
    }
    int xx=find(a[now].x);
    int yy=find(a[now].y);
    if(xx!=yy)
    {
        unionn(xx,yy);
        dfs(x,now+1,num+1);
        unionu(xx,yy);
    }
    dfs(x,now+1,num);
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].dis);
    sort(a+1,a+1+m,cmp);
    for(int i=1;i<=m;i++)
    {
        if(a[i].dis!=a[i-1].dis)
            { e[++cnt].l=i; e[cnt-1].r=i-1; }
        int xx=find(a[i].x);
        int yy=find(a[i].y);
        if(xx!=yy)
        {
            unionn(xx,yy);
            e[cnt].num++;
            tot++;
        }
    }
    e[cnt].r=m;
    if(tot<n-1) { printf("0"); return 0; }
    for(int i=1;i<=n;i++)
        fa[i]=i;
    int ans=1;
    for(int i=1;i<=cnt;i++)
    {
        sum=0;
        dfs(i,e[i].l,0);
        ans=(ans*sum)%mo;
        for(int j=e[i].l;j<=e[i].r;j++)
        {
            int xx=find(a[j].x);
            int yy=find(a[j].y);
            if(xx!=yy) unionn(xx,yy);
        }
    }
    printf("%d",ans);
}
代码

 

posted on 2018-04-13 16:53  zzzyc  阅读(101)  评论(0编辑  收藏  举报