zoukankan      html  css  js  c++  java
  • 【BZOJ 1016】[JSOI2008]最小生成树计数(搜索+克鲁斯卡尔)

    【题目链接】:http://www.lydsy.com/JudgeOnline/problem.php?id=1016

    【题意】

    【题解】

    /*
        两个最小生成树T和T';
        它们各个边权的边的数目肯定是一样的;
        且相同边权的边;
        那些边所形成的联通性是一样的;
    
        可以考虑T和T'的形成;
        比如说一开始
        T和T'都是空的;
        然后把边按边权从小到大排序后
        找到的第一种边权的边权为
        v1
        且bian[left..right]都是这种边权的边;
    
        然后假设T是我们正常用卡鲁斯卡尔算法搞出来的最小生成树;
        那么T在这个阶段肯定会在这right-left+1条边中选择x条边;
    
        那么这个时候我们再想想T'要怎么选?
        假设我们选y条边
        这里我们分类讨论一下
            假如y>x,
                这种情况是不可能的;
                因为如果y可以大于x;
                那就说明T还可以再加一条边权最小的边v1联通更多的分量;
                而我们在做克鲁斯卡尔的时候已经保证了不能添加更多的分量;
                所以可以排除;
           假如y<x
                这种情况也是不可能的;
                假设我们少选了一条边
                那么这条边链接的两个分量必然是要用更大的边权来链接;
                这就不符合最小生成树了;
                所也可以排除;
          综上可知
          x==y;
          这里所选的y条边的各个节点的连通性必然和x条边的联通性一样;
          因为如果不一样的话,
          那么哪里不一样呢??
            ->假设在T'中相对于T有两个分量没有连通在一起,那么你可以选择T中连接这两个分量的
                边啊。而且因为是升序排的,所以肯定连了更优啊;
              但是显然不可能再有这样的连法了,因为T是正确的最小生成树;
              你如果能找到这样的连法的话,就说明T是不正确的了;
              这就矛盾了;
       根据上面的推导可知;
       每个最小生成树;
       出现的不同边权的种类是一样的,且各种边权的数目是确定的;
       而且只要边权的数目确定了,这个边权造成的连通性的变化都是一样的;
    
       所以我们只要枚举对于每一种边权,可以用哪些连着不同节点的边,但边权和它相同的边交换一下
       一开始做的最小生成树的边就好了;
       比如第i种边权的边v[i],出现的次数为numv[i];
       然后在用克鲁斯卡尔算法算出的最小生成树中v[i]出现的次数为k;
       则从这numv[i]条边中选出k条边来,看看是不是这k条边中每条边都能对联通量贡献;(如果有一条边在做
       的过程中边的两段链接的联通量已经是在一起的了,就退出,表示不行);
       因为要回溯,所以这里的并查集不能加状态压缩。
       当然n也不大就是了;
        因为有说同边权的边的数目不超过10,所以2^10是可以接受的.
        搜一波;
        然后用乘法原理乘起来就好;
    */


    【完整代码】

    #include <bits/stdc++.h>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define LL long long
    #define rep1(i,a,b) for (int i = a;i <= b;i++)
    #define rep2(i,a,b) for (int i = a;i >= b;i--)
    #define mp make_pair
    #define pb push_back
    #define fi first
    #define se second
    #define rei(x) scanf("%d",&x)
    #define rel(x) scanf("%lld",&x)
    
    typedef pair<int, int> pii;
    typedef pair<LL, LL> pll;
    
    const int dx[9] = { 0,1,-1,0,0,-1,-1,1,1 };
    const int dy[9] = { 0,0,0,-1,1,-1,1,-1,1 };
    const double pi = acos(-1.0);
    const int M = 1100;
    const int N = 110;
    const int MOD = 31011;
    
    struct abc
    {
        int x, y,z;
    };
    
    int n, m,f[N],tot = 0,tot1 = 0,num[M],sum = 0,ans = 1;
    abc bian[M];
    
    bool cmp1(abc a, abc b)
    {
        return a.z < b.z;
    }
    
    int ff(int x)
    {
        if (f[x] == x) return x;
        else
            return ff(f[x]);
    }
    
    void dfs(int l, int r, int now)
    {
        if (now == num[tot])
        {
            sum++;
            return;
        }
        if (l > r) return;
        int x = bian[l].x, y = bian[l].y;
        int r1 = ff(x), r2 = ff(y);
        //这里f[r1]==r1,f[r2]==r2;
        if (r1 != r2)
        {
            f[r1] = r2;
            dfs(l + 1, r, now + 1);
            f[r1] = r1, f[r2] = r2;
        }
        dfs(l + 1, r, now);
    }
    
    int main()
    {
        //freopen("F:\rush.txt", "r", stdin);
        rei(n), rei(m);
        rep1(i, 1, m)
            rei(bian[i].x), rei(bian[i].y), rei(bian[i].z);
        sort(bian + 1, bian + 1 + m,cmp1);
        rep1(i, 1, n)
            f[i] = i;
        rep1(i, 1, m)
        {
            int l = i, r = i;
            while (r + 1 <= m && bian[r + 1].z == bian[l].z) r++;
            tot1++;
            rep1(j, l, r)
            {
                int x = bian[j].x, y = bian[j].y;
                int r1 = ff(x), r2 = ff(y);
                if (r1 != r2)
                {
                    f[r1] = r2;
                    tot++;
                    num[tot1]++;
                }
            }
            i = r;
        }
        if (tot != n - 1)
            return puts("0"), 0;
        rep1(i, 1, n)
            f[i] = i;
        tot = 0;
        rep1(i, 1, m)
        {
            int l = i, r = i;
            while (r + 1 <= m && bian[r + 1].z == bian[l].z) r++;
            tot++;
            if (num[tot] == 0)
            {
                i = r;//。。。。。。bug点
                continue;
            }
            sum = 0;
            dfs(l, r, 0);
            ans = (ans*sum) % MOD;
            rep1(j, l, r)
            {
                int r1 = ff(bian[j].x), r2 = ff(bian[j].y);
                if (r1 != r2)
                    f[r1] = r2;
            }
            i = r;
        }
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    Android TextView中文字通过SpannableString来设置超链接、颜色、字体等属性
    Android-自定义dialog
    安卓签名
    安卓 textview 换行 不满就换了
    [android] setOnTouchEvent 设置返回值为true 和 false的区别
    图片自动切换, 滑动循环切换图片
    Android访问中央气象台的天气预报API得到天气数据
    android:windowSoftInputMode属性详解
    Android中资源文件夹res/raw和assets的使用
    WPF基础学习笔记整理 (二) XAML
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7626583.html
Copyright © 2011-2022 走看看