0
点赞
收藏
分享

微信扫一扫

P 哥的桶(线段树+线性基)

以沫的窝 2022-01-26 阅读 19

线段树+线性基

链接:P4839 P哥的桶 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题意:对区间单点插入数据,求区间最大子集异或和。

题解:考虑线性基的合并,将另一个线性基的 log 个数拿出来合并入第一个线性基中,因为可以由另一个线性基合成出来的数,在把这个线性基合并进第一个线性基后,第一个线性基也可以合成出第二个线性基,再通过第二个线性基就可以合成那些数了。则可以用线段树来维护这些线性基的并。由于线性基合并复杂度为 l o g ∗ l o g log*log loglog,线段树单点修改,区间查询复杂度为 q l o g n qlogn qlogn,则整体复杂度为 q ∗ l o g n ∗ l o g 2 p q*logn*log^2p qlognlog2p,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;
}
举报

相关推荐

0 条评论