【 思维】【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的数字。
所以可以使用搜索的做法来完成这道题目。
搜索的数的类型可以分为两类
- 异或上\(2^n-1\)后的数。(凡是这类数都应该搜索)
- 异或上\(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;
}