zoukankan      html  css  js  c++  java
  • UOJ176 新年的繁荣

    题目链接

    Boruvka生成树算法

    (Boruvka)算法就是先把每个点看作一个联通块,然后不断在联通块之间找最优的边进行合并。因为每次联通块的数量最少缩小一半。所以合并次数是(log)
    先把所有的点权挂到(trie)树上。然后对于每个联通块进行合并的时候。对于联通块中的每个点都去(trie)上搜索他能找到的最优秀的边。也就是说如果当前位置是(1)那么就搜索1子树,否则的话既要搜0子树,也要搜1子树。这样1子树是一定要搜的。所以把0子树变为1子树和0子树合并起来的结果。然后就可以搜索了。
    还有一个问题就是。如果当前子树中的所有点都已经在这个联通块里了怎么办。所以统计出每棵子树中联通块编号的最大值和最小值。然后就可以知道当前子树中是不是还有不属于这个联通块里的点了。合并联通块之后再把每个点合并的联通块里的点所属的联通块修改一下就行了。
    复杂度(O((n+2^m)mlogn))

    更优秀的做法

    上面的做法代码长且思路复杂。有一种更好的做法。
    先把所有权值相同的点连一条边。这样肯定会比较优秀。
    然后考虑枚举最终答案中(w[u]&w[v])的值p。
    因为(x&w[u] leq w[u]),倒着枚举p,然后找到一个点u使得(w[u]&p=p)。然后从其他的等于满足(w[v]&p=p)的点(v)中找一个与(u)不在同一个联通块里的点。将这两个点之间连边。贡献为(p)
    万一(w[u]&w[v])(p)大呢。可以证明这是不可能的。因为p是倒着枚举的,如果(w[u]&w[v]>p)那么肯定之前就连过边了。不会再连一次。
    复杂度(O(2^mmalpha(n)))

    代码

    /*
    * @Author: wxyww
    * @Date:   2019-01-21 15:46:58
    * @Last Modified time: 2019-01-21 15:53:01
    */
    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<bitset>
    using namespace std;
    typedef long long ll;
    const int N = 1000000 +10;
    ll read() {
       ll x=0,f=1;char c=getchar();
       while(c<'0'||c>'9') {
          if(c=='-') f=-1;
          c=getchar();
       }
       while(c>='0'&&c<='9') {
          x=x*10+c-'0';
          c=getchar();
       }
       return x*f;
    }
    int p[N],fa[N];
    ll ans;
    int find(int x) {
       return fa[x] == x ? x :fa[x] = find(fa[x]);
    }
    void uni(int x,int y) {
       x = find(x),y = find(y);
       if(rand() & 1) fa[x] = y;
       else fa[y] = x;
    }
    int main() {   
       srand(time(0));
       int n = read(),m = read();
       for(int i = 1;i <= n;++i) {
          int x = read();
          if(p[x]) ans += x;
          p[x] = x;
       }
       int k = (1 << m);
       for(int i = 1;i <= k;++i) fa[i] = i;
       for(int i = (1 << m) - 1;i;--i) {
          for(int j = 0;j < m && !p[i];++j) p[i] = p[i | (1 << j)];
          int u = p[i];
          if(!u) continue;
          for(int j = 0;j < m;++j) {
             int v = p[i | (1 << j)];
             if(!v) continue;
             if(find(v) != find(u)) {
                ans += i;
                uni(u,v);
             }
          }
       }
       cout<<ans;
    
       return 0;
    }
    
  • 相关阅读:
    周4早上搜索引擎分析 crmim.com| MSCRM开发者之家
    Bat命令学习
    sqlserver日期函数
    ubunto应用软件
    sql for xml
    win7x64 连接oracle 客户端 vs 2010调试 提示“ORA12154: TNS: 无法解析指定的连接标识符 ”ORA06413 问题(转)
    CentOS Rsync服务端与Windows cwRsync客户端实现数据同步
    怎么引导2岁孩子洗手问题
    Libnfcinstallation
    Asterisk资料
  • 原文地址:https://www.cnblogs.com/wxyww/p/10299884.html
Copyright © 2011-2022 走看看