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

题目链接

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

题解

每种权值的边的数量是确定的,每种权值的边的作用是确定的
求一遍最小生成树,对于相同权值的边分组
然后对于每一种权值的边搜索,得出每组权值的边选择方案
乘法原理统计答案

代码

#include<cstdio>
#include<algorithm> 
 
inline int read() { 
    int x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar();} 
    while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = getchar(); 
    return x * f;
} 
const int maxn = 1007; 
struct Edge{int u,v,w; 
    bool operator < (const Edge &a)const { 
        return w < a.w;
    } 
}edge[maxn]; 
#define mod 31011
struct Data {
    int l,r,v;
}a[maxn]; 
int fa[maxn],n,ans = 1,sum = 0,m; 
int find(int x) { if(fa[x] != x) return find(fa[x]);else return x;  }
void dfs(int x,int now,int k) {
    if(now == a[x].r + 1) {
        if(k == a[x].v) sum ++;
        return ;
    }
    int p = find(edge[now].u),q = find(edge[now].v); 
    if(p != q) {
        fa[p] = q; 
        dfs(x,now + 1,k + 1); 
        fa[p] = p;fa[q] = q; 
    }
    dfs(x,now + 1,k); 
}   
int main() {
    n = read(),m = read();  
    for(int i = 1;i <= m;++ i) {
        edge[i].u = read();
        edge[i].v = read();
        edge[i].w = read();fa[i] = i; 
    }
    int cnt = 0,tot = 0;
    std::sort(edge + 1,edge + m + 1); 
    for(int i = 1;i <= m;++ i) {
        if(edge[i].w != edge[i - 1].w) a[cnt].r = i - 1,a[++cnt].l = i;
        int p = find(edge[i].u),q = find(edge[i].v); 
        if(p != q) {fa[p] = q;a[cnt].v++;tot++; }
    } 
    a[cnt].r = m; 
    if(tot != n - 1)puts("0");
    else {
        for(int i = 1;i <= n;++ i) fa[i] = i; 
        for(int i = 1;i <= cnt;++ i) {
            sum = 0;  
            dfs(i,a[i].l,0); 
            ans = ans * sum % mod;  
            for(int j  = a[i].l;j <= a[i].r;++ j) { 
                int p = find(edge[j].u),q = find(edge[j].v);
                if(p != q) fa[p] = q; 
            }
        }
        printf("%d\n",ans); 
    }
    return 0; 
}

posted @ 2018-05-08 15:34  zzzzx  阅读(...)  评论(... 编辑 收藏