线段树+线性基
链接:P4839 P哥的桶 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:对区间单点插入数据,求区间最大子集异或和。
题解:考虑线性基的合并,将另一个线性基的 log 个数拿出来合并入第一个线性基中,因为可以由另一个线性基合成出来的数,在把这个线性基合并进第一个线性基后,第一个线性基也可以合成出第二个线性基,再通过第二个线性基就可以合成那些数了。则可以用线段树来维护这些线性基的并。由于线性基合并复杂度为 l o g ∗ l o g log*log log∗log,线段树单点修改,区间查询复杂度为 q l o g n qlogn qlogn,则整体复杂度为 q ∗ l o g n ∗ l o g 2 p q*logn*log^2p q∗logn∗log2p,q 为操作次数,n 为序列长度,p 为值域。
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=5e4+5;
int n,m,op,u,v,w;
struct node{
int l,r,p[32];
node():l(0),r(0){memset(p,0,sizeof(p));}
}t[maxn<<2];
void insert(node&t,int x)
{
for(int i=31;i>=0;i--)
{
if(x>>i&1)
{
if(!t.p[i]){t.p[i]=x; break;}
x^=t.p[i];
}
}
return;
}
void pushup(int k)
{
memcpy(t[k].p,t[k<<1].p,sizeof(t[k].p));
for(int i=31;i>=0;i--)
{
insert(t[k],t[k<<1|1].p[i]);
}
return;
}
void build(int k,int l,int r)
{
t[k].l=l,t[k].r=r;
if(l==r)return;
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
return;
}
void update(int k,int pos,int w)
{
int x=t[k].l,y=t[k].r;
if(x==y){insert(t[k],w); return;}
int mid=x+y>>1;
if(pos<=mid)update(k<<1,pos,w);
else update(k<<1|1,pos,w);
pushup(k); return;
}
node query(int k,int l,int r)
{
int x=t[k].l,y=t[k].r;
if(l<=x&&y<=r)return t[k];
int mid=x+y>>1,flag=0; node res;
if(l<=mid)res=query(k<<1,l,r),flag=1;
if(mid<r)
{
if(!flag)res=query(k<<1|1,l,r);
else
{
node q=query(k<<1|1,l,r); res.r=q.r;
for(int i=31;i>=0;i--)insert(res,q.p[i]);
}
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>n>>m,build(1,1,m);
for(int i=1;i<=n;i++)
{
cin>>op>>u>>v;
if(op==1)update(1,u,v);
else
{
node q=query(1,u,v); int ans=0;
for(int i=31;i>=0;i--)ans=max(ans,ans^q.p[i]);
cout<<ans<<"\n";
}
}
return 0;
}