CF1174D 题解
题目意思
给出一个 \(n\) 一个 \(x\),要构造出最长的 \(a\) 满足 \(a\) 里面的任何子段的异或和不为 \(0\) 或者 \(x\)。
想法
草稿纸上的思路,即思考过程
因为题目说要求子段,这是一个比较难以 \(O{(1)}\) 计算的东西
所以我们尝试把子段变成一个好算的东西,因为他要求的是子段异或和,所以考虑如何快速求出一个段异或和
发现异或和可以通过前缀亦或得到,所以我们可以做一个异或前缀和
这个题目是一个构造类问题,所以我们如果想要判定的话,是要求不存在一个 \(pos_1,pos_2\) 满足 \(s_{pos_1}=s_{pos_2}\) 或者 \(s_{pos_1}\oplus s_{pos_2} = x\)
如果我们要让 \(s_i = x\),那么就不能 \(\exists s_j=p\) 或者 \(\exists s_j=p\oplus x\),那么发现如果存在了 \(p \oplus x\) 就不能存在 \(p\),存在了 \(p\) 就不能存在 \(p\oplus x\) 所以这两个中间只能先选择一个,那么我们贪心的选择一个就可以了。
做法
具体做法如下:我们贪心的选择小的数字加入我们的前缀和数组,同时标记选择的数字 \(i\) 和 \(i\oplus x\),之后不能再选择这两个数字,一直加入数字到没有数字能加入为止。最后通过我们的前缀和数组反推每一个数字即可。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<climits>
#include<cmath>
#define ll long long
using namespace std;
const int N=(1<<18)+9;
int n,x,vis[N],ans[N],cnt;
int main(){
cin>>n>>x;
vis[x]=1;
for(int i=1;i<(1<<n);i++){
if(vis[i]) continue;
vis[i^x]=1;
ans[++cnt]=i;
}
cout<<cnt<<endl;
for(int i=1;i<=cnt;i++)
cout<<(ans[i]^ans[i-1])<<' ';
return 0;
}

浙公网安备 33010602011771号