Title

【 思维】【DFS联通块】CF986C AND Graph

【 思维】【联通块】CF986C AND Graph

题目大意

给定两个数n和m。

现有一个有m个整数构成的集合,每个整数均在0~2^n-1之间,以每个整数为顶点建立一个无向图,当两个整数x,y符合“x&y=0”时,则认为x,y之间存在一条边。这里的“&”表示运算符“且”。计算图中联通块的数量。

题目思路

如果x与上y等于0的话,从微观的角度来考虑的话,x的每一位与上对应的y的每一位都会等于0.

如果x在某一位上为1的话,那么y在那一位上必须为0。

如果x在某一位上为0的话,那么y在那一位上既可以为0,也可以为1。

不妨规定n=5,且设\(p=2^n-1=31=11111\)

不妨设\(x = 10011\),\(y=x\space xor\space p =01100\)

发现\(x \& y=0\)

且不妨令\(y_1=01000,y_2=00100\)

发现\(x \& y_1=0,x\&y_2=0\)

所以x与上将y在某一个位置本为1后变为0的数\(y_i\)也是符合答案的

\(y_i\) 也有相对应的可以与起来变成0的数字。

所以可以使用搜索的做法来完成这道题目。

搜索的数的类型可以分为两类

  1. 异或上\(2^n-1\)后的数。(凡是这类数都应该搜索)
  2. 异或上\(2^n-1\)后的数的所有的子数(可以每次单变一个1为0就好)(凡是这类数都值得被搜索)

其他分析

观察一下数据范围,\(0≤n≤22 , 1 \le m \le 2^{n} \le4194304 \le5e6\)

代码

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
using namespace std;
const int N = 5E6+500;
int cnt,p; 
int n,m;
bool used[N],mark[N];
vector<int > vec;
void dfs(int u)
{
	used[u] = 1;
	mark[u] = 0;
	if( mark[ u^p ] && !used[ u ^ p ] ) dfs( u^p );
	//子集 
	for(int i=0;i<n;i++)
		if( u & (1<<i) )
		   if( !used[ u ^ (1<<i) ]) dfs( u ^ (1<<i) );
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    p = (1<<n)-1;
    for(int i=1;i<=m;i++)
    {
    	int x;
    	cin>>x;
    	vec.push_back(x);
    	mark[ x ^ p ] = 1;
	}
	
	for(int i=0;i<vec.size();i++)
	    if(!used[ vec[i] ]) 
		{
		   mark[ vec[i] ^ p] = 1;
		   used[ vec[i] ] = 1;
		   dfs( vec[i] ^ p );	
		   cnt++;
		}
    cout<<cnt;
	return 0;
}
posted @ 2022-02-23 19:22  BeautifulWater  阅读(65)  评论(0编辑  收藏  举报