HDU-3949 XOR 题解报告
题目地址
题意:
从一个序列中取任意个元素进行异或,求能异或出的所有数字中的第 k 小。
分析:
性质:一个线性基异或上另一个不同的线性基,并作为自己的新值,这并不改变整个线性基的性质(线性基只有元素数量是固定的)。
从大到小枚举每一个线性基d[i],从它的第二高位往低位扫,如果扫到d[i]在第j位是1,那么就异或上d[j](d[j]最高位是1),这样就消去d[i]在第j位上的1。当然d[ j ]这个向量可能不存在。那么原本该位的1不变。那么处理完一个线性基之后,应该大致是长这个样子的(x表示0或1):
1xxxx0xxx0x 1xxx0x 1x
也就是,每个线性基元素的最高位1,只会由自己贡献,这依旧满足线性基基本性质。我们把这些新元素放入新数组_base,无视旧数组中的空向量,记新数组大小为cnt。_base数组具有与二进制基数一样特点:
- 每个向量选或不选;
- 往一个集合中加入新向量,或者把集合中一个元素换做更大的向量,集合异或和会变大;
- 比向量X小的所有向量的异或和,仍小于X。
如果k
的第 i 位为1,那么结果就异或上 _base[i],最后便得到了第 k
小。
额外补充:
给予一组元素,每个数互异且遍布了2的幂,即 k
小的子集的大小。很显然,这个第k
小子集的大小就是 k 本身。比如k
是(1001),结果便是(
如果我们去掉某些权位,比如得到 k
小。那么,对于序列的所有子集的值,在二进制表示下,位权缺失的位都为0,无视这些位后仍可视作二进制数。于是我们可以直接剔除缺失部分,以其他数代位。对于k
是(1001),结果便为(
代码:
#include<bits/stdc++.h> using namespace std; using ll = long long; int main() { cin.tie(0)->sync_with_stdio(false); int T; cin>>T; for(int NO=1;NO<=T;++NO) { cout<<"Case #"<<NO<<":\n"; ll base[65] = {0}; int zero=0; int n; cin>>n; for(int i=1;i<=n;++i) { ll x; cin>>x; for(int j=63;j>=0;--j) { if(x>>j) { if(base[j]) x^=base[j]; else{ base[j]=x; break; } } } if(x==0) zero=1; } vector<ll> b; for(int i=63;i>=0;--i) { if(!base[i]) continue; for(int j=i-1;j>=0;--j) { if((base[i]>>j)&1) { base[i]^=base[j]; } } b.push_back(base[i]); } reverse(b.begin(),b.end()); int m = b.size(); int q; cin>>q; while(q--) { ll k; cin>>k; if(k>=(1LL<<m)+zero) { cout<<"-1\n"; continue; } k-=zero; ll ans=0; for(int i=0;i<64;++i) { int r=((k>>i)&1); if(r) ans^=b[i]; } cout<<ans<<"\n"; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】