P4839 P哥的桶 题解(线段树维护线性基)

题目链接

题目大意

(n)个桶,(m)次操作

操作分为两种

  • (pos)桶中加入一个(val)

  • ([l,r])中选任意个桶使得异或和最大,求最大的异或和

注意每个节点是一个桶可以放多个值

(n,mleq 5 imes 10^4)

题目思路

单点修改,区间查询,异或最大值

很显然是线段树维护线性基

然后这样的复杂度是(O(n imes log^3))

然而会(TLE)

看了一下大佬的题解,更新的时候没有必要维护两个子节点的并集

只需要从上往下更新,每到一个节点就更新其线性基维护的值就好了。

查询的时候每次都将不同区间所维护的线性基合并到答案即可

插入复杂度(O(n imes log^2))

查询复杂度(O(n imes log^3))

但是这个查询的常数还是大大优化了

TLE代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef pair<double,int> pdi;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=5e4+5,mod=1e7;
int n,m;
int a[maxn];
int p[40];
vector<int> tree[maxn<<2];
void ins(int x) {
    for(int i = 30;i>=0; i--) {
        if (!(x &(1<<i)))  continue;
        // x的第i位是0
        if(!p[i]){
            p[i] = x;
            break;
        }
        x^= p[i];
    }
}
vector<int> mer(vector<int> a,vector<int> b){
    vector<int> ans;
    memset(p,0,sizeof(p));
    for(int i=0;i<a.size();i++) ins(a[i]);
    for(int i=0;i<b.size();i++) ins(b[i]);
    for(int i=0;i<=30;i++){
        if(p[i]!=0){
            ans.push_back(p[i]);
        }
    }
    return ans;
}
void update(int node,int pos,int l,int r,int val){
    if(l==r){
        vector<int> temp;
        temp.push_back(val);
        tree[node]=mer(tree[node],temp);
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=pos) update(node<<1,pos,l,mid,val);
    else  update(node<<1|1,pos,mid+1,r,val);
    tree[node]=mer(tree[node<<1],tree[node<<1|1]);
}
vector<int> query(int node,int L,int R,int l,int r){
    if(L<=l&&r<=R){
        return tree[node];
    }
    vector<int> ans;
    int mid=(l+r)/2;
    if(mid>=L) ans=mer(ans,query(node<<1,L,R,l,mid));
    if(mid<R)  ans=mer(ans,query(node<<1|1,L,R,mid+1,r));
    return ans;
}
signed main(){
    scanf("%d%d",&m,&n);
    for(int i=1,opt,pos,val,l,r;i<=m;i++){
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&pos,&val);
            update(1,pos,1,n,val);
        }else{
            scanf("%d%d",&l,&r);
            vector<int> ans=query(1,l,r,1,n);
            int pr=0;
            for(int j=(int)ans.size()-1;j>=0;j--){
                pr=max(pr,pr^ans[j]);
            }
            printf("%d
",pr);
        }
    }
    return 0;
}

AC代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
typedef pair<double,int> pdi;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=5e4+5,mod=1e7;
int n,m;
int a[maxn];
int tree[maxn<<2][40];
void ins(int pos,int x) {
    for(int i = 30;i>=0; i--) {
        if (!(x &(1<<i)))  continue;
        // x的第i位是0
        if(!tree[pos][i]){
            tree[pos][i] = x;
            break;
        }
        x^= tree[pos][i];
    }
}
void update(int node,int pos,int l,int r,int val){
    ins(node,val);
    if(l==r){
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=pos) update(node<<1,pos,l,mid,val);
    else  update(node<<1|1,pos,mid+1,r,val);
}
void query(int node,int L,int R,int l,int r){
    if(L<=l&&r<=R){
        for(int i=0;i<=30;i++){
            if(!tree[node][i]) continue;
            ins(0,tree[node][i]);
        }
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=L) query(node<<1,L,R,l,mid);
    if(mid<R)  query(node<<1|1,L,R,mid+1,r);
}

signed main(){
    scanf("%d%d",&m,&n);
    for(int i=1,opt,pos,val,l,r;i<=m;i++){
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d",&pos,&val);
            update(1,pos,1,n,val);
        }else{
            scanf("%d%d",&l,&r);
            memset(tree[0],0,sizeof(tree[0]));
            query(1,l,r,1,n);
            int pr=0;
            for(int i=30;i>=0;i--){ // tree[0]为答案
                pr=max(pr,pr^tree[0][i]);
            }
            printf("%d
",pr);
        }
    }
    return 0;
}

卷也卷不过,躺又躺不平
原文地址:https://www.cnblogs.com/hunxuewangzi/p/14547433.html