题目传送门
题目大意
每个点有一个点权$a_i$,连接点$u$和点$v$的边的边权是$a_u\ and\ a_v$,问最大生成树。
我们从大到小考虑每一种边权,如果两个未连通的点的点权同时包含这个边权作为子集,显然用这样的边连接这些点最优。
因此每一种已经考虑过的边权,我们用并查集取一个代表元。
每枚举到一个新的边权就尝试合并一些已有的连通块。
Code
1 /** 2 * uoj 3 * Problem#176 4 * Accepted 5 * Time: 1128ms 6 * Memory: 3276k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 #ifndef WIN32 12 #define Auto "%lld" 13 #else 14 #define Auto "%I64d" 15 #endif 16 using namespace std; 17 typedef bool boolean; 18 19 #define ll long long 20 21 template <typename T> 22 void pfill(T* pst, const T* ped, T val) { 23 for ( ; pst != ped; *(pst++) = val); 24 } 25 26 int n, m; 27 int S; 28 int *f, *ar; 29 ll res = 0; 30 31 int find(int x) { 32 return (f[x] == x) ? (x) : (f[x] = find(f[x])); 33 } 34 35 inline void init() { 36 scanf("%d%d", &n, &m); 37 S = 1 << m; 38 f = new int[S]; 39 ar = new int[S]; 40 pfill(ar, ar + S, 0); 41 for (int i = 1, x; i <= n; i++) { 42 scanf("%d", &x); 43 if (!ar[x]) 44 f[x] = x, ar[x] = x; 45 else 46 res += x; 47 } 48 } 49 50 inline void solve() { 51 for (int s = S - 1, x, y; s; s--) { 52 for (int i = 0; !ar[s] && i < m; ar[s] |= ar[s | (1 << (i++))]); 53 if (!(x = ar[s])) 54 continue; 55 for (int i = 0; i < m; i++) 56 if (ar[s | (1 << i)]) { 57 y = find(ar[s | (1 << i)]); 58 if (y ^ find(x)) { 59 f[y] = find(x), ar[s] = find(x); 60 res += s; 61 } 62 } 63 } 64 printf(Auto, res); 65 } 66 67 int main() { 68 init(); 69 solve(); 70 return 0; 71 }
原文地址:https://www.cnblogs.com/yyf0309/p/9880278.html
时间: 2024-10-07 05:23:01