题目链接:传送门

Description

给定mm个均小于2n2^n的数,其中若满足x&y=0x \& y = 0则x和y之间有一条边。问连完所有边后连通块个数

Hint

0n22,1m2n0 \leq n \leq 22, 1\leq m \leq 2^n

Solution

套路建图题。 之前cxr大佬讲过一个类似的题目,思路差不多 总共建m+2nm+2^n个点,m个给出的点记为(x,1)(x,1),另外建2n2^n个点,记为(x,2)(x,2) 考虑这样连边:

对于所给的m个点从(x,1)(x,1) 连向 (x,2)(x,2)
对于所有的x从(x,2)(x,2)连向(x(1<<k),2)(x|(1 << k),2),其中k满足x的第k位为0
对于所给的m个点从(x,2)(\sim x,2)连向(x,1)(x,1) ~x为x在2n2^n意义下取反

证明比较显然,就不赘述了 不用真正把边都连出来,直接dfs即可

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <bits/stdc++.h>

using namespace std;

const int Maxn = (1 << 22) + 1000;

int A[Maxn], P[Maxn], N, M, PP[Maxn];

inline void dfs (int x)
{
int REV = ((1 << N) - 1) ^ x;
if (P[REV]) return ;
P[REV] = 1;
if (PP[REV]) dfs(REV);
for (int i = 0; i < N; ++i)
{
if (!(x & (1 << i)))
{
dfs(x | (1 << i));
}
}
}

int main()
{
#ifdef hk_cnyali
freopen("F.in", "r", stdin);
freopen("F.out", "w", stdout);
#endif
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; ++i){ scanf("%d", &A[i]); PP[A[i]] = 1;}
int cnt = 0;
for (int i = 1; i <= M; ++i)
{
if (!P[A[i]])
{
++cnt;
dfs(A[i]);
}
}
cout<<cnt<<endl;
return 0;
}