BZOJ 1016 最小生成树计数

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=1016

相关博客

https://blog.csdn.net/sdfzyhx/article/details/52075151

kur产生的最小生成树只是一组解,但不一定是唯一的解。

(具体分析不写了)有一个结论,任意最小生成树中,固定边权选取的数量是一定的,只是相同的边权,选取的方式不同,造成的多个解。

   然后对于相同的边权,子集枚举满足的方案数数量。最后根据乘法原理求解。

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int mxn=10000;
int n,m;
int sum;
int ans=1;
//
struct edge{//
    int x,y;
    int v;
}e[mxn];
struct segment{
    int st,ed;//区块起止点 
    int v;
}se[mxn];
int cnt;
int cmp(const edge a,const edge b){
    return a.v<b.v;
}
//
int fa[mxn]; 
int find(int x){
    if(fa[x]==x)return x;
    return find(fa[x]);//不可压缩 
}
//
void dfs(int x,int now,int t){//x 组编号   now现在处理的边编号  t使用的边编号 
    if(now==se[x].ed+1){
        if(t==se[x].v)sum++;
        return;
    }
    int u=find(e[now].x),v=find(e[now].y);
    if(u!=v){
        fa[u]=v;
        dfs(x,now+1,t+1);//选用这条边 
        fa[u]=u;fa[v]=v;//还原状态 
    }
    dfs(x,now+1,t);//不选这条边 
    return;
}

int main(){
    scanf("%d%d",&n,&m);
    int i,j;
    for(i=1;i<=n;i++)fa[i]=i;//初始化并查集,处理边的连通 
    for(i=1;i<=m;i++)
        scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
    sort(e+1,e+m+1,cmp);
    int tot=0;//联通边数 
    for(i=1;i<=m;i++){
        if(e[i].v!=e[i-1].v){//如果权值与之前不同 
            se[cnt].ed=i-1;se[++cnt].st=i;//分到新的一组 
        }
        int u=find(e[i].x);
        int v=find(e[i].y);
        if(u!=v){fa[u]=v;se[cnt].v++;tot++;}
    }
    se[cnt].ed=m;
    if(tot!=n-1){printf("0");return 0;}//未联通
    for(i=1;i<=n;i++)fa[i]=i;//初始化并查集,处理边组的连通
    for(i=1;i<=cnt;i++){
        sum=0;
        dfs(i,se[i].st,0);
        ans=(ans*sum)%31011;
        for(j=se[i].st;j<=se[i].ed;j++){
            int u=find(e[j].x),v=find(e[j].y);
            if(u!=v)fa[u]=v;
        }
    }
    printf("%d",ans%31011);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/stranger-/p/9905979.html