题意就是给我们两个数n,x。让我们构造个数组,数组有三个条件
1.要有尽可能多的元素
2.要其中任何一段数字的异或和不等于0和x
3.元素的范围是[ 1, 2n2^n2n)
分析:如果对异或足够敏感的话 应该能想到其中第二个条件的意思,其实就是说目标数组中的元素的异或前缀和不能有相等的两个数,如果有相等的两个异或和说明其中可以中间的一段数是可以用异或得到0的,也就是违反了条件2。
那么对于x,其实就是如果异或和中有一个x,说明这其中有段数的异或和等于x,所以违反2,那么也就是说其中任意两个前缀和数字异或不能为x。
那么也就是我们可以枚举[ 1, 2n2^n2n)中的每一个数,然后对其中每一个数i进行处理如果当前的i^x的数之前出现过作为异或和数组中的元素了,根据异或运算的性质:
a^b = c,
那么
c^a = b,
c^b = a
所以就说明,i^x之前出现过,那么如果我们要把i确定为最新的异或和数组中的元素,就会形成
i^x ^ i = x的异或性质,那么违反规则2.
所以本题我们可以根据异或的特点,将这个问题转化为构造异或前缀和数组,而数组的构造方法是根据异或的性质来的,如果a^x ^a = x 就不选这个数
最后根据异或数组ai-1^ai得到那个原数组元素
如果异或熟练,这个解法可以很快想到
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int maxn =1<<19;
int cnt,S[maxn];
int main()
{int n,x;scanf("%d%d",&n,&x);// 不能存在两相等 也不能存在和x异或为0的数set<int>s;s.insert(0);for(int i=1;i<(1<<n);i++){if(!s.count(i^x)){//已存在一个0 如果与x相等会得到0 保证不会有相同的元素 尽可能变大三个条件s.insert(i);S[++cnt]=i;}}printf("%d\n",cnt);for(int i=1;i<=cnt;i++){printf("%d",S[i]^S[i-1]);if(i==cnt)puts("");else putchar(' ');}return 0;
}