线性基
导引问题:给出 个数: ,请问:
取其中的一些数进行异或操作,能否得到值 ?
# 前置知识:异或运算
# 性质
相异为 ,相同为
如果
则有: 和
小问题
给出 个整数,除了 个数外,其余 个数均成对出现,请编程找出这个单独的数
Answer
累异或,成对的都消掉了
有 个数组成的数列,包含 这 种整数,也就是说,有一个数是重复出现的,求其值
Answer
解:异或整个数列异或( )
给出 个整数,除 个数外,其余 个数均成对出现,清编程找出这 个不成对的数
Answer
解:做两遍异或,两个单独的数将整个数列分为两大类
# 线性基
# 定义
对于一个数集 ,它的线性基是一个最小的数集 ,使得A中任意一个数可以通过 中的一些数异或得到
对于一个数集,他可能存在不止一个线性基
在某些涉及异或都操作中,线性基可以代替原集合,从而达到减小数据规模的目的
# 举例
对于一个集合
它的线性基可以是:
# 如何求线性基
通过线性代数的知识,求出极大线性无关组
线性基的关键在于降低规模
若 ,则意味着 可被其他已选的数表示,所以就去掉 (不把 当作线性基元素)
思考:为什么要是求得的线性基最高位不同?
Answer
便于计算出 是否能被现有的数表示。
e.g.
问: 线性基能否表示 ?
由于
每一位来看,异或起来可以变成
线性基的形式:
为了方便,设置d[i]表示非0最高位为右数第i位的数
// 对于 a[1].a[2].....a[n] 的线性基
ll d[61];
for ( int i = 1; i <= n; i ++ ) { // 枚举这个序列
ll x = a[i];
for ( int j = 60; j >= 0; j -- ) { // 从高位向下看看还需不需要再构造东西了
if ( x & (1ll << j) ) { // 如果a[i]这一位是"1"
if ( !d[j] ) { d[j] = x; break; } // 如果d[j]也没有构造过就构造一下
else x ^= d[j]; // 否则通过异或将这一位去掉
}
}
}
2
3
4
5
6
7
8
9
10
11
# 线性基的性质
线性基没有值为 的元素 ( 因为 在异或中不起作用 )
线性基元素中任意一个都无法通过另外的线性基元素异或得到
原集合中任意一个都可以通过线性基中某些数异或得到
# 问题解决
#
回看导引问题
Answer
将这n个数求出线性基,将询问的值通过线性基后为0则表示可以表示,否则无法表示
inline bool check ( ll x ) {
for ( int j = 60; j >= 0; j -- ) {
if ( x & (1ll << j) ) {
if ( !d[j] ) return false;
else x ^= d[j];
}
}
return (x == 0);
}
2
3
4
5
6
7
8
9
#
给出 个数, ,问取出其中一些数进行异或操作,能够得到的最大的数是多少
Answer
用最简形式的线性基异或
1.将这 个数求出线性基
2.将答案初始化为
3.从最高位向最低位查询,若当前答案该位(第位)为 且 ,则当前答案异或
4.所有位查询以后得到的答案就是最大值
inline ll Find ( ) {
ll res = 0;
for ( int i = 60; i >= 0; i -- ) {
if ( ((1ll << i) & res) == 0 && d[i] ) res ^= d[i]; // 该位相反为1
}
return res;
}
2
3
4
5
6
7
#
给出 个数, ,问取出其中一些数进行异或操作,能够得到的最小的数是多少
Answer
将这n个数求出线性基
在构造线性基的过程中,若存在一个数插入线性基时插入失败,即插入后 ,那么最小值为
若答案不为 ,则答案为最小可确定位所表示的 ,即:从低位往高位看,如果一个位置的 ,则答案就是该
#
给出 个数,,问取出其中的一些数进行异或操作,能够得到的第 小的数是多少(去重过)
Answer
求出最简线性基
观察 ,我们只能在 时自由选择第 位为 或者为
由于线性基无法异或出 ,特盘是否会出现异或为 的情况,若出现则
对于能够选择的位,我吗可以确定 或者 。那我们将 二进制表示,若某一位为 ,则可以选择位的值置为 ,否则置为
这里可以采用k的二进制去求:
我们从 下标 开始往上照到第一个不为 的线性基
如果 这一位是 ,就把找到的对应线性基异或进答案,否则就不异或进去
然后 整除 ,继续这一过程