网易的一道面试题
读入一串操作,操作分两类。格式为1 x或 2 x。
当读入操作1时,将x压入集合A中。
当读入操作2时,查探是否有A的一个子集,将子集中的所有数做“或运算”的结果等于x,如果有则输出YES, 如果没有输出NO.
思想是当读入1的时候,将所有可能出现的情况全部记录下来;当读入2的时候,直接去查询现有的记录是否满足条件。
这里用到了如何枚举集合的子集的技巧。
枚举集合的子集的方法:https://www.cnblogs.com/Emerald/p/4695672.html
//枚举s的子集 for(int i=s;i;i=(i-1)&s){ //具体操作 }
答案参考:https://www.nowcoder.com/discuss/216237
#include<bits/stdc++.h> using namespace std; // 对于每一次询问,我们肯定会选择所有y,满足y&x==y,即在二进制表达下,y是x的子集。 // 那么我们可以维护一个数组p[i],表示所有为i的子集的数字的Or值是多少。 // 每次插入一个数字,如果它没有重复出现过,就枚举它的超集更新答案。询问的时候只需要看p[x]是否等于x就好了。 int p[131072] ; int mx = 131071 ; //二进制(1111111111111)转化为十进制为131071 int q ; int main(){ scanf("%d",&q) ; while(q--) { int op , x; scanf("%d%d",&op,&x) ; if(op == 1) { if(p[x] == x) continue ; int s = mx ^ x; //对x取反 for(int i=s ; i ; i=(i-1)&s) { //枚举s的子集 p[i ^ x] = p[i ^ x] | x ; //i^x 为集间差 } p[x] = x; } else { if(p[x] == x) puts("YES") ; else puts("NO"); } } return 0; }