zoukankan      html  css  js  c++  java
  • 线性基习题小结

    洛谷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)$

  • 相关阅读:
    错因集锦
    组合数学12
    硬币购物
    考试套路整理
    考前模板整理
    我的友链
    P4127 [AHOI2009]同类分布
    P1836 数页码_NOI导刊2011提高(04)
    P4124 [CQOI2016]手机号码
    数位DP小结
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11579351.html
Copyright © 2011-2022 走看看