• 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
    ", ans);
        return 0;
    }
    
    
  • 相关阅读:
    非专业码农 JAVA学习笔记 3 抽象、封装和类(1)
    非计算机专业的码农C#学习笔记 三、变量 表达式 字符串
    非专业码农 JAVA学习笔记 2 java语言基础
    非计算机专业的码农C#学习笔记 五、数组和集合
    PSP个人软件开发工具
    端口映射
    $.proxy()
    input type=button设置高度不管用
    移动端日期控件
    mac终端下svn常用命令
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8478961.html
Copyright © 2020-2023  润新知