CF986C AND Graph

搜索+思维好题

我们考虑在什么情况先x&y=0,因为&相当于乘法也就是说两个1相见的时候才是1,那么我们可以得到,x,y二进制表达之后,x有1的位置,y必定都是0,x是0的位置y可以是0也可以是1,比如x=10011(假设x的位数已经是最大),那么y最大就是01100,也可以将y上任意一个1换成0,也就是说可以将y拆成{00100,01000,00000}所以我们可以得出,对于一个还没有加入任何联通块的数字,将他按位取反后,在将1任意取出,都是可以和当前数字链接的,取反如何做呢,我们发现最大上限为(1<<n)-1,所以x取反+x=(1<<n)-1,所以用这个数-x就是x取反,然后对于取反,我们枚举取出每一位,然后用更短的二进制去更新连通性

代码

//By AcerMo
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> 
using namespace std;
const int M=(1<<22)+50;
bool vis[M],mark[M];
int ans=0,n,m,a[M];
int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
int main()
{
	queue<int>q;
	n=read();m=read();
	for (int i=1;i<=m;i++) 
		a[i]=read(),mark[a[i]]=1;//标记这个数存在 
	for (int i=0;i<(1<<n);i++)
	{
		if ((!mark[i])||vis[i]) continue;//判断存在或已被使用 
		ans++;vis[i]=1;//没用过就是新联通块 
		int emm=(1<<n)-1-i;//却反
		if (!vis[emm]) q.push(emm);//看取反是不是被用过,被用过那么根据对称性,当前数一定被搜过 
		vis[emm]=1;
		while (q.size())
		{
			int u=q.front();q.pop();
			for (int k=0;k<n;k++)
			{
				if (u&(1<<k)) //这位存在1 
				{
					int qlm=u^(1<<k);//将这一位抹去	 
					if (!vis[qlm])//判断是否搜到过
					{
						vis[qlm]=1;q.push(qlm);//继续搜索
						if (mark[qlm])//存在与原数列 
						{
							int gll=(1<<n)-1-qlm;//取反去更新 
							if (!vis[gll]) vis[gll]=1,q.push(gll);	
						} 
					} 
				}
			}
		} 
	}
	cout<<ans;
	return 0; 
}

猜你喜欢

转载自blog.csdn.net/acerandaker/article/details/80945269