BZOJ1016&&洛谷P4208 [JSOI2008]最小生成树计数

emmm数据太水,直接爆搜

有一个定理:无论最小生成树由哪些边组成,其中使用的每种边权的边的数量是一定的

所以由此我们可以得到一种做法:记录最小生成树使用的每种边的数量,然后dfs使用这些边去构建另一棵最小生成树

代码

//By AcerMo
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=2050;
const int mod=31011;
struct edge{int from,to,cost;}emm[M];//存原图构树 
bool cmp(edge a,edge b){return a.cost<b.cost;}
struct nmap{int from,to,num;}map[M];//记录最小生成树种每种边权的数量 
int n,m,cnt=0,all=0;
int fa[M],siz[M];
int find(int x)
{
    if (x!=fa[x]) return find(fa[x]);
    return x;
}
void dfs(int x,int now,int tot)
{
    if (now==map[x].to+1)
    {
        if (tot==map[x].num) all++;
        return ;
    }//无论树如何,在保证最小生成树的情况下,每种边权的边的数量不变	
    int r1=find(emm[now].from);
    int r2=find(emm[now].to);
    if (r1!=r2)
    {
        fa[r1]=r2;
        dfs(x,now+1,tot+1);
        fa[r1]=r1;fa[r2]=r2;
    }//合并以当前边权的边为连接的的联通块 
    dfs(x,now+1,tot);
    return ;
}
void kthtree()
{
    for (int i=1;i<=n;i++) fa[i]=i;int ans=1;
    for (int i=1;i<=cnt;i++)
    {
        dfs(i,map[i].from,0);
        ans=(ans*all)%mod;
        all=0;
        for (int k=map[i].from;k<=map[i].to;k++)
        {
            int r1=find(emm[k].from);
            int r2=find(emm[k].to);
            if (r1!=r2) fa[r1]=r2;
        }
    }//乘法原理
    cout<<ans;
    return ;
}
void kru()
{
    for (int i=1;i<=n;i++) fa[i]=i;
    int tot=1,sum=1;
    for (int i=1;i<=m;i++)
    {
        if (emm[i].cost!=emm[i-1].cost){map[++cnt].from=i;map[cnt-1].to=i-1;}
        //记录每种边权的边在边权数组中的区间 
		int r1=find(emm[i].from);int r2=find(emm[i].to);
        if (r1!=r2) fa[r1]=r2,map[cnt].num++,tot++;//记录熟练 
    }//最小生成树 
    if (tot<n){puts("0");return ;}
    map[cnt].to=m;kthtree();
    return ;
}
int main()
{
    scanf("%d%d",&n,&m);int a,b,c;
    for (int i=1;i<=m;i++)
        scanf("%d%d%d",&emm[i].from,&emm[i].to,&emm[i].cost);
    sort(emm+1,emm+m+1,cmp);kru();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值