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;
}

posted @ 2025-07-23 08:46  Lordreamland  阅读(12)  评论(0)    收藏  举报