uoj 176 新年的繁荣 - 贪心 - 并查集

题目传送门

  传送门

题目大意

  每个点有一个点权$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 }

猜你喜欢

转载自www.cnblogs.com/yyf0309/p/9880278.html