线段树套线性基 P4839 P哥的桶

线段树每个结点维护一个线性基,插入时直接插入,查询时把所有被查询区间所包含的区间的线性基插入到一个大的线性基里,最后在大的线性基里查询就好了。

\(O(n\log m\log ^2x)\)

对于单点修改,给从根节点到单点的路径每个点都插入这个数。

对于区间查询,把所有答案插入到一个大的线性基里,在这个线性基里查最大异或和即可。


#define ls u << 1
#define rs u << 1 | 1
const int N = 5e4 + 10;

struct LinearBasis{ // 每个节点维护一个线性基
    int a[32];
    inline void insert(int u){ 
        for(int i = 31; ~ i; i --){
            if(u & (1ll << i))
                if(!a[i]){ a[i] = u; return; }
                else u ^= a[i];
        }
    }
    inline void insert(LinearBasis &n){ // 在当前节点插入另一个线性基
        for(int i = 31; ~ i; i --)
            if(n.a[i]) insert(n.a[i]);
    }
}p[N << 2];

inline void insert(int u, int l, int r, int k, int x){ // 根到当前节点路径上每个线性基都插入该元素
    p[u].insert(x);
    if(l == r) return;
    int mid = l + r >> 1;
    if(k <= mid) insert(ls, l, mid, k, x);
    else insert(rs, mid + 1, r, k, x);
}

LinearBasis ans;

inline void query(int u, int l, int r, int ql, int qr){
    if(ql <= l && qr >= r){ ans.insert(p[u]); return; } // 将线性基插入答案线性基
    int mid = l + r >> 1;
    if(qr <= mid) query(ls, l, mid, ql, qr);
    else if(mid < ql) query(rs, mid + 1, r, ql, qr);
    else query(ls, l, mid, ql, mid), query(rs, mid + 1, r, mid + 1, qr);
}

signed main(){
    int n, m;
    read(n), read(m);
    for(int i = 1; i <= n; i ++){
        int op, a, b;
        read(op), read(a), read(b);
        if(op == 1) insert(1, 1, m, a, b);
        else{
            memset(ans.a, 0, sizeof(ans.a));
            query(1, 1, m, a, b);
            int maxn = 0;
            for(int i = 31; ~ i; i --) // 在答案线性基中查询最大值
                if((maxn ^ (ans.a[i])) > maxn)
                    maxn ^= ans.a[i];
            print(maxn), puts("");
        }
    }
    return 0;
}
posted @ 2022-07-18 21:57  Altwilio  阅读(48)  评论(0编辑  收藏  举报