线性基习题小结

洛谷P4839“P的桶”

•题意

  有 m 个桶,依次编号 1~m;

  初始,这 m 个桶全部为空桶;

  给你 n 次操作,每次操作有两种:

    (1)1 k x : 往 k 桶中加入数

    (2)2 l r : 查询 $[l,r]$ 对应数的异或最大值;

•题解

  很常规的一道线性基题目;

  用线段树维护,对于操作 1,找到第 k 个桶在线段树中的位置 pos,将 x 插入到 pos 对用的线性基中即可;

  pushUp(pos) 的话,你可以选择每次向上回溯的时候将 pos 的左右儿子的线性基合并;

  其实不用这么做,因为每次只在 k 处添加一个数,直接在 update() 回溯的时候,将当前位置 pos 的线性基中插入 x 即可;

•Code

  洛谷P4839.cpp


CodeForces1100F"Ivan and Burgers"

•题意

  给你 n 个数,m 此操作,每次操作选择一个区间,求这个区间异或最大值;

•题解

  感觉这道题上一道题差不多,都是求区间异或最大值;

  但是,此题的数据范围 $nleq 5^5$,用线段树维护线性基的方式是会超时的;

  解法1:离线处理

    将所有询问收集起来,并按照 r 升序排列;

    边插入 $a_i$ 边判断 i 是否为当前询问的右端点;

    插入的时候,记录两个数值 $base_i,p_i$,表示第 $p_i$ 个数 $a_{p_i}$ 在通过 $Insert()$ 操作时,插入到了 $base_i$ 中;

    每次插入第 i 个数 $a_i$ 时,优先让高位的 1 用当前的位置 i 来表示;

  CodeForces1100F(离线).cpp

  解法2:在线处理

    理解了离线处理的方式,在线处理很容易就能理解;

    就是在之前的基础上多开了一维;

    定义 $base[i][]$ 表示按照上述离线方式插入前 i 个数后形成的基底,$p[i][]$ 表示按照上述离线方式插入前 i 个数后,$base[i][]$ 对应的插入下标;

  CodeForces1100F(在线).cpp


HDU3949"XOR"

•题意

  给你 n 个数,q 此询问,每次询问给出一个数 k;

  求经这 n 个数异或后得到的第 k 小的数,不存在输出 -1;

•题解

  首先将这 n 个数插入到线性基 $base$ 中;

  然后,需要改造 $base$,使其满足如下要求:

    对于任意存在于线性基的二进制位 i,至多只有一个 $base_j$ ​满足第 i 位为 1。

  将改造后的线性基中的非零值按照升序的方式存入 $th$ 中;

 1 vector<ll >th;
 2 void reBuild()
 3 {
 4     th.clear();
 5     for(int i=60;i >= 0;--i)
 6         for(int j=i-1;j >= 0;--j)
 7             if(base[i]>>j&1)
 8                 base[i] ^= base[j];
 9     for(int i=0;i <= 60;++i)
10         if(base[i])
11             th.push_back(base[i]);
12 }

  这 n 个数共有 $siz=th.size()$ 个非零基底;

  那么,共有 $2^siz -1$ 个组合方式,所以,这 $siz$ 个非零基底共可形成 $2^siz-1$ 个不同的数;

  由于线性基是不能表示 0 这个数的,所以需要单独判断这 n 个数是否可以通过异或得到 0;

  如果可以,那么这 n 个数经过异或可得到 $2^siz$ 个不同的数,反之,仅能得到 $2^siz-1$ 个不同的数;

  判断是否包含 0,直接判断 siz 和 n 的大小关系即可;

  如果 siz == n ,说明这 n 个数全部插入到了线性基中,也就意味着得不到 0;

  反之,可以得到 0;

  得到 $th$ 后,如何得到第 k 小的数呢?

  公式:$kth=oplus k_icdot th_i$,其中 $k_i$ 指的是 k 转化成二进制后的第 i 位;

  不难用二进制的思想证明;

  HDU3949.cpp

•题目变形

  给你 n 个数,q 次询问,每次询问给出三个数 $l,r,k$;

  求 $[l,r]$ 区间异或第 k 小的值,不存在输出 -1;

•题解

  $[l,r]$ 区间的第 k 小,你需要知道的是 $a_l,a_{l+1},cdots ,a_r$ 这 $r-l+1$ 个数重构后的线性基;

  每次都从 l 循环到 r 求解肯定不行;

  借鉴上述 CodeForces1100F 在线的思想,提前预处理出 $[1,r]$ 的线性基即可;

  每次重构的时候,只需遍历 $base$ 中有限的 60 个数就行;

  HDU3949(变形).cpp

  时间复杂度 $O(mcdot 60^2)$

原文地址:https://www.cnblogs.com/violet-acmer/p/11579351.html