CodeForces 986C AND Graph(构图+DFS)

C. AND Graph
time limit per test:4 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

You are given a set of size m with integer elements between 0 and 2n1 inclusive. Let's build an undirected graph on these integers in the following way: connect two integers x and y with an edge if and only if x&y=0. Here & is the bitwise AND operation. Count the number of connected components in that graph.

Input

In the first line of input there are two integers n and m (0n22, 1m2n).

In the second line there are m integers a1,a2,,am (0ai<2n) — the elements of the set. All ai are distinct.

Output

Print the number of connected components.

Examples
Input
Copy
2 3
1 2 3
Output
Copy
2
Input
Copy
5 5
5 19 10 20 12
Output
Copy
2
Note

Graph from first sample:

Graph from second sample:


        大致题意:给你m个数字,这m个数字都小于2^n。对于任意的数字i、j,如果i&j==0,那么连接i与j,现在问最后连完所有的编后,总共有多少个连通块。

        直接去考虑还是不太好想,还是从数据范围出发。n为22,2^n约等于400W,所以说大致需要一个O(m*2^n)的算法。我们考虑一下构图,对于每一个数字x,我们知道~x表示x的补集,二者的与值为0,显然把~x的部分1换成0再与x按位与的值也是0。那么对应回来如果x|(1<<k)与y的结果为0,那么x与y的结果也为0,其中k表示x在二进制下为0的位。因为~(x|(1<<k))是~x的部分1换成之后的结果,所以可以有这样一个传递的关系。于是我们建边x->x|(1<<k)。另外,对于每一个输入的数字i,如果~i能够到达,那么表示i这个点会与另外一个输入的数字j的与的结果为0。那么根据连通块的传递性,所有和i做与运算结果为0的点j都能到达,于是相应的也要连边~i->i。

        如此以来,我们以每个没有被访问过的输入数字为起点走dfs,最后跑dfs的次数就是最后的答案。问题的关键在于,如何证明如果任意两个点在图中能够相互到达等价于两个点在同一个连通块。下面进行证明。

        首先是充分性,对于任意的点a和b,如果存在路径a->x1->x2->...->b,其中a和b都是输入的数字。情况一,这x1x2...xn都不是输入的数字,那么b的前驱一定是~b,而且每一步往后走其实时每次把a的一个0变成1,根据之前证明的,显然a&b==0,a和b在同一个连通块中;情况二,中间有输入的数字,那么我们定位到b左边第一个输入的数字c,根据情况一有c&b==0且任意两个相邻的输入数字的按位与为0,再根据连边的定义,要么是每次把一个0变成1,要么是按位与结果为0的相互传递,所以a和b在同一个连通块中。

        其次是必要性。如果两个数字a和b按位与结果为0,那么根据位运算规则,一定有a&(~b)==a,也即~b是a二进制下某些位置的0变成1之后的结果,再根据构图的定义,满足这个条件那么从a出发一定能够到达~b。有由于b时输入的数字,所以也有连边~b->b。因此,存在路径a->x1->x2->...->b

        证明完毕,我们就可以从每一个没被访问的点出发dfs,dfs的次数就是最后的答案。另外,由于本题空间限制,不能够把边存储下来,所以每次再dfs的时候在线枚举边。具体见代码:

#include <bits/stdc++.h>
#define N 1<<22
using namespace std;

int a[N],total,n,m;
bool v[N],p[N];

void dfs(int x)
{
    v[x]=1;
    for(int i=0;i<n;i++)
        if ((x&(1<<i))==0&&!v[x^(1<<i)]) dfs(x^(1<<i));
    if (p[total^x]&&!v[total^x]) dfs(total^x);
}

int main()
{
	cin.tie(0);
	ios::sync_with_stdio(0);
	cin>>n>>m; total=(1<<n)-1;

    for(int i=1;i<=m;i++)
        {cin>>a[i];p[a[i]]=1;}
    int ans=0;
    for(int i=1;i<=m;i++)
        if (!v[total^a[i]]) {ans++; if (!v[a[i]])dfs(a[i]);}
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/80524913