CF1902F:Trees and XOR Queries Again 题解(前缀线性基)

F:

题意:一棵树,每个点有点权,多次询问x到y的路径上的线性基能不能组成z。

Solution:

好像有很多倍增啊或者是点分治来解决线性基的合并问题的,不太想学,因为发现了一个叫前缀线性基的黑科技,只加了一个同样大小的数组,就可以用n个线性基表示任意区间的线性基。

前缀线性基模板题:CF1100F:Ivan and Burgers。就是给出序列 a,每次询问 l 到 r 的线性基能构成的最大值。

我们对于线性基的每一个数位的基向量 \(d[i]\),另外设一个 \(pos[i]\) 表示这个数位最后由谁更新,对于 \(l\)\(r\) 的询问,我们的区间线性基相当于第 \(r\) 个线性基只保留那些 \(pos[i]>=l\) 的基向量。

也就是说,我们只需要求出n个前缀线性基,然后每个前缀的每个后缀(任意区间),可以由这个前缀线性基中的 pos 数组来区分。

pos 怎么更新维护:压入一个新数字时,传入其值 x 与位置 wei ,若某个数位向量空缺,直接 \(d[i] = x, pos[i] = wei\);若某个数位存在向量但是 \(pos[i]<wei\) ,则 \(swap(d[i],x); swap(pos[i],wei);\) 虽然不是特别好理解,但是嗯~,这么做想想不无道理。

板子代码放下面啦:

struct PLB{

    int d[22], pos[22];

    void insert(int x,int wei) {
        for(int i=21;i>=0;i--) {
            if(!((x>>i)&1)) continue;
            if(!d[i]) {
                d[i] = x;
                pos[i] = wei;
                return ;
            }
            if(pos[i]<wei) {
                swap(pos[i],wei);
                swap(d[i],x);
            }
            x ^= d[i];
        }
    }

    int query_max(int l) {    // 求最大值
        int ans = 0;
        for(int i=21;i>=0;i--)
            if(d[i] && pos[i]>=l)
                ans = max(ans,ans^d[i]);
        return ans;
    }

    bool query(int k,int l) {  // 能否构成k
        for(int i=21;i>=0;i--) {
            if(!(k>>i)) continue;
            if(!d[i] || pos[i]<l) return 0;
            k ^= d[i];
        }
        return 1;
    }

}B[N];

那么回到这道题,序列变到了树上。

每个点求出前缀线性基,即到祖先路径的线性基。

保留 x 到 lca 的线性基,就是x的前缀线性基中所有 \(pos>dep[lca]\) 的向量。再求下 y 到 lca 的线性基,两个合在一起(把一个线性基的所有向量往另一个里面塞),就是 x 到 y 路径的线性基了。

my submission

posted @ 2024-01-18 00:15  maple276  阅读(25)  评论(0编辑  收藏  举报