UOJ176 新年的繁荣
不会 Boruvka 。
如果两个点权值相同,他们互相连边肯定是最优的,谁都不会有损失。于是先给这样的点连边,最后只剩下权值互不相同的点。
然后考虑从高到底枚举\(i\)表示\(x\&y=i\)。但是不可能枚举\(x\)和\(y\)。
于是考虑对于每个出现过的权值,将他的二进制下子集的父亲设为他,相当于将自己的状态传递到他的子集。
那么对于我们枚举的每一个\(i\),如果他是某个权值二进制下的子集,他一定是该权值的子孙。由于我们只留下了权值互不相同的点,只需要枚举\(i|j\),如果与\(i\)还未合并,将二者合并(如果已经合并说明之前拿过更优的贡献了,就不用管了),然后加上\(i\)的贡献。
感觉这是一个与 Kruskal 类似的过程,我们尽量拿大的贡献,直到所有点联通。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x
const int N = 5e6 + 10;
const int mod = 1e9 + 7;
const ll INF = 1e18;
int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }
int n, m, fa[N];
ll res = 0;
int get(int x){
if(fa[x] == x) return x;
else return fa[x] = get(fa[x]);
}
signed main(){
n = read(), m = read();
for(int i = 1; i <= n; i++){
int x = read();
res += fa[x];
fa[x] = x;
}
for(int i = (1 << m) - 1; i >= 0; i--){
for(int j = 0; j < m && fa[i] == 0; j++) fa[i] = fa[i | (1 << j)];
if(! fa[i]) continue;
for(int j = 0; j < m; j++){
int x = fa[i], y = fa[i | (1 << j)];
if(! y) continue;
int fx = get(x), fy = get(y);
if(fx != fy){
fa[fx] = fy;
res += i;
}
}
}
cout << res;
return 0;
}
$\color{blue} \mathcal {Lordreamland}$

浙公网安备 33010602011771号