zoukankan      html  css  js  c++  java
  • 线性基性质证明和应用

    线性基是由原集合构造出的一个集合
    在线性基中选取任意多个数,异或起来,能且只能表示出原集合中选取任意多个数异或起来,得出的数
    且元素个数是在满足上述要求的条件下最少

    设当前要插入的数是 (x),线性基集合用 (a_i) 表示,则构造方法:

    • (x) 最高的一个为 (1) 的二进制为是 (i)
    • 如果 (a_i) 没有数,则令 (a_i=x),结束插入
    • 如果 (a_i) 已经有数了,则插入 (a_i operatorname{XOR} x),这样 (x) 最高二进制位肯定会被置为 (0)

    模版题:https://www.luogu.com.cn/problem/P3812
    插入代码:

    inline void insert(long long x){
    	for(reg int i=50;~i;i--)if(x&(1ll<<i)){
    		if(!a[i]){a[i]=x;return;}
    		else x^=a[i];
    	}
    }
    

    一些性质和证明,性质大部分和 oi-wiki 上的相同,但加入了一些证明:

    1. 这样插入就能保证其中元素能通过异或和,来表示原集合任意数的异或和
      • (x) 最终没有赋值给任意 (a_i),那么 (x) 和一系列 (a_i) 异或起来得 (0),那么 (x) 就等于这些 (a_i) 异或起来
      • (x) 最终赋值给了 (a_j),则 (a_j) 异或 (x) 赋值给 (a_j) 之前被异或上的所有 (a_i),就是 (x)
      • 那么我们就能用线性基中的数表示出每个被插入的数了
      • 那么也就能表示出任意多数的异或和
      • 又由于插入的数要么是原集合中的数,要么是若干个原集合中的数的异或和;所以线性基中的若干个数的异或和也只能表示出原集合中若干个书的异或和
    2. 线性基中去任意非空集合,异或和不为 (0)
      • (a_i operatorname{XOR} a_j operatorname{XOR} cdots =0)(a_i) 是最后插入的一个),则 (a_i=a_j operatorname{XOR} cdots),那么 (a_i) 不会被插入进来
    3. 线性基每个元素的二进制最高位互不相同,显然;由此也得出元素个数是 (log S)
    4. 线性基中不同集合的异或和均不同
      • 假设 (operatorname{XOR}_{i=1}^n a_{p_i}=operatorname{XOR}_{i=1}^m a_{q_i})
      • 那么两边同时异或上 (operatorname{XOR}_{i=2}^n a_{p_i}),就得到了 (a_{p_1}=(operatorname{XOR}_{i=1}^n a_{p_i}) operatorname{XOR} (operatorname{XOR}_{i=1}^m a_{q_i}))
      • (a_{p_1}) 被表示为了一系列线性基中其他数的异或和,那么 (a_{p_1}) 显然不会被插入(如果它是在这些数中最后被插入是这样,其实只要让它们中最后插入的那个数来当 (a_{p_1}) 就行了)
    5. 线性基中的数个数是一定的(也就是变换插入顺序个数不会变)
      • 首先,如果原集合中每个数都被插入进了线性基,则显然成立
      • 如果插入顺序是 (a,b,c,x,cdots),且 (x) 没有被成功插入,就是 (a operatorname{XOR} b operatorname{XOR} c =x)
      • 那么如果改变顺序为 (a,x,b,c)(a,b,x,c) 等,(x) 就会被成功插入,但又会有某个数插入失败
      • 所以个数是一定的
    6. 线性基是满足性质 1 的最小集合
      • 暂时还不会证,也没找到资料/kel

    应用

    找最大值

    从高到低(这里和以下说的“从高到低”,都是指对于线性基中元素最高二进制位从高到低)考虑每个元素,如果 (ans) 的这一位(只此元素最高的二进制位)为 (0),则 (ans) 异或上这个元素
    其实用到贪心的思想,考虑高位的时候先不管低位如何

    inline long long max(){
    	long long ans=0;
    	for(reg int i=50;~i;i--)if(!(ans&(1ll<<i))) ans^=a[i];
    	return ans;
    }
    

    查看一个数是否可以用原集合的异或和表示出

    就是把 (x) 异或上线性基中某些数,变成 (0)
    还是从高到低考虑每个元素,如果 (x) 的这一位是 (1),那么 (x) 肯定要异或这个数(由性质 3)
    结束后看 (x) 是不是 (0)

    查询 (k) 小值

    https://loj.ac/p/114

    由于性质 3 和 4,可以知道,如果异或得到的数最高二进制位为 (i),则 (a_i) 必须被异或,(a_0)(a_{i-1}) 可异或可不异或,(a_j,(j>i)) 不能异或
    那么一共能得出 (2^i) 个数;不过,这是 (a_0)(a_{i-1}) 都有数的情况,实际上应该是 (2^m)(m) 表示 (a_0)(a_{i-1}) 中有数的元素个数
    考虑这 (2^m) 个数中的最小值,可以用类似于上面求最大值的方法求出,设个最小值是 (w_m)
    那么,如果要求 (2^n+2^n) 小的数,设 (w_m,w_n) 分别对应第 (i,j) 位((i>j)),那么就先让 (i) 位为 (1),就是变成了求在此基础上的第 (2^n)
    那么再让 (j) 位为 (1),就是求此基础上的最小了,那么进而就可以知道 (w_m operatorname{XOR} w_n) 即为所求
    由此,只要对 (k) 做二进制拆分,答案就是若干个 (w) 异或起来

    另外,由于线性基异或不出 (0),还要考虑一下原集合能不能异或出 (0) 的问题,代码中用 _0 表示
    代码:https://loj.ac/s/1048752

    合并两个线性基

    显然直接将其中一个的数全部插入到另一个中就行了

    题目

    P3292 [SCOI2016]幸运数字
    倍增,设 (base(i,u)) 表示从 (u) 一直到它的 (2^i) 级祖先(不包括),的 (2^i) 个数构成的线性基
    然后每次跳倍增数组然后给线性基合并就可以了
    空间 (O(nlog n log S)),时间 (O(nlog nlog ^2 S))

    P4151 [WC2011]最大XOR和路径
    找出所有的环,把换上权值的异或和加入线性基
    考虑从 (1)(n) 走一个路径,显然可以经过所有环(走到环上,再走回来,两段不在环上的路一异或没了)
    随便找一个从 (1)(n) 的无环路径,在线性基中找出它的权值异或和,异或上若干元素的最大结果就行了
    为什么是随便一个路径?因为如果有一个以上路径,随便选的这个路径和最优路径也会形成一个环,只要再异或上这个环就好了

    别的题不太会写,我好菜啊/kk

  • 相关阅读:
    天气渐凉,意渐浓
    WebBrowser 多线程 DocumentCompleted 和定时器
    浅谈 Glide
    浅谈 maxMemory , totalMemory , freeMemory 和 OOM 与 native Heap
    XGoServer 一个基础性、模块完整且安全可靠的服务端框架
    GreenDao 兼容升级,保留旧数据的---全方面解决方案
    基于 xorm 的服务端框架 XGoServer
    全面总结: Golang 调用 C/C++,例子式教程
    通俗易懂,各常用线程池的执行 流程图
    为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/14351451.html
Copyright © 2011-2022 走看看