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

题面

传送门

Sol

最小生成树的性质:

  • 对于每一个\(MST\),每一种边权所使用的边数相同
  • 所有\(MST\)中边权\(≤w\)的边组成的图的连通性相同

那么这道题就枚举没个权值选那些边,如果连的个数和原来的相同就统计
最后乘法原理即可

如果同边权过多就只能用矩阵树定理了
然而我太菜了不会。。

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
# define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
using namespace std;
typedef long long ll;
const int Zsy(31011);
const int _(105);
const int __(1005);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, m, fa[_], ans = 1, o[__], len, l[__], r[__], id[__], cnt[__], tmp[_];
struct Edge{
    int u, v, w;

    IL bool operator <(RG Edge B) const{
        return w < B.w;
    }
} edge[__];

IL int Find(RG int x){
    return x == fa[x] ? x : fa[x] = Find(fa[x]);
}

IL int Count(RG int x){
    RG int ret = 0;
    for(; x; x -= x & -x) ++ret;
    return ret;
}

int main(RG int argc, RG char* argv[]){
    n = Input(), m = Input();
    for(RG int i = 1; i <= n; ++i) fa[i] = i;
    for(RG int i = 1; i <= m; ++i){
        edge[i] = (Edge){Input(), Input(), Input()};
        o[i] = edge[i].w, l[i] = m, r[i] = 1;
    }
    sort(edge + 1, edge + m + 1);
    sort(o + 1, o + m + 1), len = unique(o + 1, o + m + 1) - o - 1;
	RG int gg = 0;
    for(RG int i = 1; i <= m; ++i){
        RG int fx = Find(edge[i].u), fy = Find(edge[i].v);
        id[i] = lower_bound(o + 1, o + len + 1, edge[i].w) - o;
        l[id[i]] = min(l[id[i]], i), r[id[i]] = max(r[id[i]], i);
        if(Find(fx) == Find(fy)) continue;
        fa[fx] = fy, ++cnt[id[i]], ++gg;
    }
	if(gg != n - 1) return puts("0"), 0;
    for(RG int i = 1; i <= n; ++i) fa[i] = i;
    for(RG int i = 1; i <= len; ++i){
        RG int tot = 0;
        for(RG int j = 1; j <= n; ++j) tmp[j] = fa[j];
        for(RG int j = 0, S = 1 << (r[i] - l[i] + 1); j < S; ++j){
            if(Count(j) != cnt[i]) continue;
            RG int flg = 0;
            for(RG int k = 1; k <= n; ++k) fa[k] = tmp[k];
            for(RG int k = 0; k <= r[i] - l[i]; ++k)
                if((1 << k) & j){
                    RG int fx = Find(edge[k + l[i]].u), fy = Find(edge[k + l[i]].v);
                    if(fx == fy){
                        flg = 1;
                        break;
                    }
                    fa[fx] = fy;
                }
            if(!flg) ++tot;
        }
        ans = ans * tot % Zsy;
        for(RG int j = 1; j <= n; ++j) fa[j] = tmp[j];
        for(RG int j = l[i]; j <= r[i]; ++j){
            RG int fx = Find(edge[j].u), fy = Find(edge[j].v);
            if(fx != fy) fa[fx] = fy;
        }
    }
    printf("%d\n", ans);
    return 0;
}

posted @ 2018-02-27 15:02  Cyhlnj  阅读(172)  评论(0编辑  收藏  举报