https://codeforces.com/contest/1174/problem/D
题意:
给出两个整数n,x,构造一个满足下列条件的数组 a: 1<=ai<2^n (次方), 不存在a的非空子段其异或和为0或x,数组的长度要最大化。 输出数组长度和每个元素。
子段的含义是原数列删去开头的n个数字和结尾的n个数字得到的新数列(n可以为0)
思路:
如果直接构造答案数组a,会发现a中数字可以重复,实在无从下手,我们需要采用前缀和思想。
构造出一个前缀异或和数组b,bi表示前i个数字的前缀异或和,则al⊕al+1⋯⊕ar=bl−1⊕br。这样问题就转化为了构造这样一个数组,其中没有一对数字的异或和为0或x,并使数组长度最大。
而要满足没有一对数字的异或和为0,只需要不让同一个数字出现两次即可。
如果x>=2^n,没有任何一对小于2^n的数字异或和能得到x,而前缀异或和最大也不会等于2^n,所以我们可以把区间[1,2^n-1]的所有整数任意顺序放入b数组中
else 有些数字对的异或和会得到x,那么在构造b数组的时候,只能将这些数字对的其中任意一个数字放入数组,所以当我们从1到2^n-1遍历能否把数字i放入b数组的时候,要注意如果i⊕x得到的数字已经在之前出现过,或者i就是x,那么表示该数字i不能再加入b数组,
最后不要忘记b[0]=0,表示前0个数字的异或和为0. 使用公式ai=bi⊕bi−1ai=bi⊕bi−1.即可得到每个ai
此代码也可以只有else后面的部分,不用管x多大,都检测一遍
#include<bits/stdc++.h> using namespace std; const int maxn=1<<18; int a[maxn]; int ex[maxn]; int main(){ //freopen("datain.txt", "r", stdin); ios::sync_with_stdio(0); cin.tie(0); int n,x; cin>>n>>x; if(x>=(1<<n)){ vector<int> v; v.push_back(0); cout<<(1<<n)-1<<endl; for(int i=1;i<(1<<n);i++) v.push_back(i); int sz=v.size(); for(int i=1;i<sz;i++) cout<<(v[i]^v[i-1])<<' '; cout<<endl; return 0; } else { vector<int> v; v.push_back(0); ex[0]=1; for(int i=1;i<(1<<n);i++) if(!ex[i^x]){ ex[i]=1; v.push_back(i); } cout<<v.size()-1<<endl; int sz=v.