// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css //目录导航 //生成目录索引列表

#724 D【位运算】

题目描述

给你n个数

然后对于每一个数 \(a_i\) ,判断是否存在一个 \(a_j\) ,使得 \(a_i \& a_j = 0\)

输入格式

第一行一个数 \(n\)

然后 \(n\) 个整数,表示 \(a_i\)

输出格式

一行,n个整数,表示答案

如果存在,输出1

否则,输出0

样例

【样例输入】

6 

1 2 3 5 7 9

【样例输出】

1 1 0 1 0 1

基本思想

一开始我的想法是使用 \(trie\) 树储存所有 01串,然后使用 \(DFS\) 逐个查找。貌似使用了 \(trie\) 树会是查询更高效,实则我们想避开所有 1 ,会使 \(DFS\) 查询变成事实上的穷举\(trie\) 树的插入步骤的时间复杂度是 \(O(nL)\) ,但是查询操作仅仅对于每个数而言会有 \(O(2^L)\) ,这与暴力配对没有实质区别。

那么我们考虑是否可以将每个 01串可以符合的预期串预处理出来,也就是处理出它的补集,意即 0010--> 1101,1101 & 0010=0,但是我们在 1101 中删去几个 1 仍然是符合条件的,这就是我们预处理的方向。乍一看这个复杂度还是挺高的,但问题是题目要求我们判断存在,我们预处理出来之后查询是 \(O(1)\) 的,更何况我们从 \(2^{20}\) 向下遍历,我们在补集中删去 1 只会使数变小,得到的新数会向后加入进行再次处理,意思就是说我们不需要考虑枚举要删去多少个 1 ,而只需要考虑删去那个 1 即可。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=1<<20;
int n,m,a[N];
bool ok[M+10];
int main(){
    ios::sync_with_stdio(false);
	cin>>n;
	m=M-1;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ok[a[i]^m]=true;
	}
	for(int i=m;i>=0;i--)if(ok[i]){
		for(int j=0;j<=20;j++)if(i&(1<<j))
			ok[i^(1<<j)]=true;
		//去除1只会变小,会加到后面进行再次处理 
	}
	for(int i=1;i<=n;i++){
		cout<<ok[a[i]]<<" ";
	}
	return 0;
}
posted @ 2025-08-05 10:07  SSL_LMZ  阅读(7)  评论(0)    收藏  举报