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

 

posted @ 2017-02-26 01:17  WDZRMPCBIT  阅读(...)  评论(... 编辑 收藏