1. 程式人生 > >51Nod1577 異或湊數 線性基

51Nod1577 異或湊數 線性基

原文連結https://www.cnblogs.com/zhouzhendong/p/51Nod1577.html

題意

  給定一個長度為 n 的序列。

  有 m 組詢問,每一組詢問給出 L,R,k ,詢問 L,R 區間內是否能找出一些數,使它們 XOR 起來等於 k 。

  $n,m\leq 5\times 10^5,  0\leq a_i,k< 2^{30}$

題解

  由於 $n,m$ 同階,所以以下時間複雜度描述時,對於 $n,m$ 不加區分。

  線性基合併是 $O(\log ^2 a_i)$ 的。

  直接線段樹維護區間線性基或者 ST 表複雜度均為 $O(n\log ^3 a_i)$ 。

  CDQ分治時間複雜度為 $O(n\log ^2 a_i)$ 。

  以上演算法均不能通過。

  考慮將詢問離線,按照 R 從小到大排序。

  我們將 $a_i$ 從左到右依次加入。利用線性基維護儘量靠右的基向量即可(經典套路)。

  時間複雜度為 $O(n\log a_i)$ 。

程式碼

#include <bits/stdc++.h>
using namespace std;
int read(){
	int x=0;
	char ch=getchar();
	while (!isdigit(ch))
		ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=500005;
int n,m;
int a[N];
struct xxj{
	int v[30],p[30];
	void clear(){
		memset(v,0,sizeof v);
		memset(p,0,sizeof p);
	}
	void insert(int x,int y){
		for (int i=29;i>=0;i--)
			if (~x>>i&1)
				continue;
			else if (!v[i]){
				v[i]=x,p[i]=y;
				break;
			}
			else {
				if (y>p[i])
					swap(y,p[i]),swap(x,v[i]);
				x^=v[i];
			}
	}
	int query(int x,int y){
		for (int i=29;i>=0;i--)
			if (x>>i&1)
				if (!v[i]||p[i]<y)
					return 0;
				else
					x^=v[i];
		return 1;
	}
}xianxingji;
struct Query{
	int L,R,k,id,ans;
}q[N];
bool cmpR(Query a,Query b){
	return a.R<b.R;
}
bool cmpid(Query a,Query b){
	return a.id<b.id;
}
int main(){
	n=read();
	for (int i=1;i<=n;i++)
		a[i]=read();
	m=read();
	for (int i=1;i<=m;i++){
		q[i].L=read();
		q[i].R=read();
		q[i].k=read();
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmpR);
	xianxingji.clear();
	for (int i=1,j=0;i<=m;i++){
		while (j<q[i].R)
			j++,xianxingji.insert(a[j],j);
		q[i].ans=xianxingji.query(q[i].k,q[i].L);
	}
	sort(q+1,q+m+1,cmpid);
	for (int i=1;i<=m;i++)
		puts(q[i].ans?"YES":"NO");
	return 0;
}