【洛谷P5607】无力回天 NOI2017

题目

题目链接:https://www.luogu.com.cn/problem/P5607
给你一个长度为 n 的整数序列 \(a_1\), \(a_2\), \(\ldots\), \(a_n\) ,你需要实现以下两种操作,每个操作都可以用四个整数 \(opt\;l\;r\;v\) 来表示:
\(opt=1\) 时,代表把一个区间 \([l,r]\) 内的所有数都 xor 上 \(v\)
\(opt=2\) 时, 查询一个区间 \([l,r]\) 内选任意个数(包括 \(0\) 个)数 xor 起来,这个值与 \(v\) 的最大 xor 和是多少。
\(n,m\leq 5\times 10^4,a_i\in[0,10^9]\)

思路

区间异或最大值。考虑线性基。
因为线性基合并是 \(O(\log^2 a)\) 的,所以考虑再套上一个 \(\log\) 数据结构维护区间线性基。显然采用线段树。
但是这个区间异或操作很烦,我们无法支持线性基内的数全部异或上 \(v\) 这种操作。我们必须把区间修改转化为单点修改。
所以我们令 \(a'_i=a_i\text{ xor }a_{i-1}\),这样我们每次区间异或上 \(v\) 就只需要让 \(a'_l\)\(a'_{r+1}\) 异或上 \(v\) 了。那么 \(a_i=\text{xor}^{i}_{j=1}a'_j\),可以在线段树上 \(O(\log n)\) 得到。
那么再回来考虑区间查询:不难发现,\(a_l\sim a_r\) 所表示的线性基,与 \(a'_{l+1}\sim a'_{r}∪a_l\) 所表示的线性基是一样的。那么直接线段树上合并线性基,然后插入 \(a_l\),最后查询与 \(v\) 异或起来的最大值即可。
时间复杂度 \(O(m\log n\log a^2)\)
可能有些卡常,我记 \(cnt\) 表示一个线性基插入的元素个数,如果 \(cnt=\log a\) 那么直接返回不用插入了。这样快了整整 \(1s\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=50010,LG=30;
int n,m,a[N];

struct Xor
{
	int cnt,d[LG+1];
	
	void ins(int x)
	{
		if (cnt==LG) return;
		for (int i=LG;i>=0;i--)
			if (x&(1<<i))
			{
				if (!d[i]) { d[i]=x; cnt++; return; }
				x^=d[i];
			}
	}
	
	int query(int res)
	{
		for (int i=LG;i>=0;i--)
			if (!(res&(1<<i)) && d[i]) res^=d[i];
		return res;
	}
	
	void merge(Xor lc,Xor rc)
	{
		Xor x;
		memset(x.d,0,sizeof(x.d)); x.cnt=0;
		for (int i=LG;i>=0;i--)
			x.ins(lc.d[i]),x.ins(rc.d[i]);
		memcpy(d,x.d,sizeof(d)); cnt=x.cnt;
	}
}b[N*4],c;

struct SegTree
{
	int val[N*4];
	
	void pushup(int x)
	{
		b[x].merge(b[x*2],b[x*2+1]);
		val[x]=val[x*2]^val[x*2+1];
	}
	
	void build(int x,int l,int r)
	{
		if (l==r)
		{
			val[x]=a[l]; b[x].ins(val[x]);
			return;
		}
		int mid=(l+r)>>1;
		build(x*2,l,mid); build(x*2+1,mid+1,r);
		pushup(x);
	}
	
	void update(int x,int l,int r,int k,int v)
	{
		if (l==r)
		{
			memset(b[x].d,0,sizeof(b[x].d)); b[x].cnt=0;
			val[x]^=v; b[x].ins(val[x]);
			return;
		}
		int mid=(l+r)>>1;
		if (k<=mid) update(x*2,l,mid,k,v);	
			else update(x*2+1,mid+1,r,k,v);
		pushup(x);
	}
	
	int query1(int x,int l,int r,int k)
	{
		if (l==r) return val[x];
		int mid=(l+r)>>1;
		if (k<=mid) return query1(x*2,l,mid,k);
			else return query1(x*2+1,mid+1,r,k)^val[x*2];
	}
	
	void query2(int x,int l,int r,int ql,int qr)
	{
		if (ql<=l && qr>=r)
		{
			c.merge(c,b[x]);
			return;
		}
		int mid=(l+r)>>1;
		if (ql<=mid) query2(x*2,l,mid,ql,qr);
		if (qr>mid) query2(x*2+1,mid+1,r,ql,qr);
	}
}seg;

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=n;i>=1;i--) a[i]^=a[i-1];
	seg.build(1,1,n);
	while (m--)
	{
		int opt,l,r,v;
		scanf("%d%d%d%d",&opt,&l,&r,&v);
		if (opt==1)
		{
			seg.update(1,1,n,l,v);
			if (r<n) seg.update(1,1,n,r+1,v);
		}
		else
		{
			memset(c.d,0,sizeof(c.d)); c.cnt=0;
			c.ins(seg.query1(1,1,n,l));
			if (l<r) seg.query2(1,1,n,l+1,r);
			printf("%d\n",c.query(v));
		}
	}
	return 0;
}
posted @ 2021-02-19 10:46  stoorz  阅读(33)  评论(0编辑  收藏  举报