bzoj1016: [JSOI2008]最小生成树计数

最小生成树+dfs。

首先可知某一特定权值的边的数量在不同的最小生成树是确定的。(可以用反证法yy一下)

这样先用kruskal算法求最小生成树,一边统计某种边用的数量。

然后dfs一下(就是枚举每条边有没有,因为相同权值的边最多只有10条,所以是O(2^n)的枚举可以胜任)。

同时要注意图是否联通,不联通输出0.

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100 + 10;
const int maxm = 2000 + 10;
const int mod = 31011;

struct Edge {
    int u,v,w;
} e[maxm];

int n,m,p,cnt,tot,cur,res=1;

struct S {
    int f[maxn];
    
    int find(int x) {
        return f[x]==x?x:f[x]=find(f[x]);
    }
    
    bool merge(int u,int v) {
        int ru=find(u),rv=find(v);
        if(ru != rv) {
            f[ru]=rv;
            return true;    
        }
        return false;
    }
    
    void init(int n) {
        for(int i=1;i<=n;i++) f[i]=i;
    }
} s1,s2;

bool cmp(Edge a,Edge b) {
    return a.w < b.w;    
}

void dfs(int l,int r,int cnt,int &cur) {
    if(l>r) {
        if(cnt==0) cur++;
        return;
    }
    S tmp = s2;
    if(s2.merge(e[l].u,e[l].v)) dfs(l+1,r,cnt-1,cur);
    s2=tmp;
    dfs(l+1,r,cnt,cur);
}

void build() {
    scanf("%d%d",&n,&m);
    s1.init(n); s2.init(n);
    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,cmp);
    
    tot=n,p=1;
    for(int i=1;i<=m+1;i++) {
        if(e[i].w != e[i-1].w) {
            dfs(p,i-1,cnt,cur=0);
            res=res*cur%mod;
            p=i; cnt=0; s2=s1;    
        }
        if(s1.merge(e[i].u,e[i].v)) {
            cnt++; tot--;    
        }
    }
    
    if(tot==1) printf("%d\n",res);
    else printf("0\n");
}

int main() {
    build();
    return 0;    
}
posted @ 2016-05-20 16:49  invoid  阅读(...)  评论(... 编辑 收藏