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 路径的线性基了。