首页 > 代码库 > BZOJ1016 JSOI2008 最小生成树计数 生成树+DFS

BZOJ1016 JSOI2008 最小生成树计数 生成树+DFS

题意:求最小生成树的方案数,保证每个边权出现的次数小于十次。
题解:首先我们需要知道:一张图对于一个确定的边权,在任意最小生成树中出现的次数是相同的(请不要问我为什么QAQ)。所以我们先求出每一种边权在MST中出现的次数,然后枚举每一个边权,暴力看取哪些边可以组出一颗MST,复杂度O(M*2^10*M/10)

技术分享
#include <cstdio>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;const int Mod=31011;const int MAXN=100+2;const int MAXM=1000+2;struct Edge{    int u,v,w;    friend bool operator<(Edge x,Edge y){ return x.w<y.w;}}e[MAXM],t[MAXM];int N,M,f[MAXN],C=1,Ans,S;int Find(int x){ return x==f[x]?x:Find(f[x]);}void DFS(int x,int l,int w){    if(l==t[x].v+1){        if(w==t[x].w) S++;        return;    }    int a=Find(e[l].u),b=Find(e[l].v);    if(a!=b) f[a]=b,DFS(x,l+1,w+1),f[a]=a,f[b]=b;    DFS(x,l+1,w);}int main(){    cin >> N >> M;    for(int i=1;i<=M;i++) scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);    sort(e+1,e+M+1);    for(int i=1;i<=N;i++) f[i]=i;    for(int i=1;i<=M;i++){        if(e[i].w!=e[i-1].w) t[C].v=i-1,t[++C].u=i;        int a=Find(e[i].u),b=Find(e[i].v);        if(a!=b) f[a]=b,t[C].w++,Ans++;    }    t[C].v=M;    if(Ans<N-1){        cout << 0 << endl;        return 0;    }    Ans=1;    for(int i=1;i<=N;i++) f[i]=i;    for(int i=1;i<=C;i++){        S=0,DFS(i,t[i].u,0),Ans=Ans*S%Mod;        for(int j=t[i].u;j<=t[i].v;j++){            int a=Find(e[j].u),b=Find(e[j].v);            if(a!=b) f[a]=b;        }    }    cout << Ans << endl;    return 0;}
View Code

 

BZOJ1016 JSOI2008 最小生成树计数 生成树+DFS