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;
}
posted @ 2025-10-11 15:39  zacharyzhongyq  阅读(17)  评论(0)    收藏  举报