#trie#A 区间异或

题目

给定一个长度为\(n\)的序列,询问有多少个\((l,r),1\leq l\leq r\leq n\)满足

\[xor_{l\leq j\leq r}a_j\geq k \]


分析

显然跑一次前缀和就变成查询两个数的异或值是否不少于\(k\)
那么这显然可以转换成01trie的问题
按照二进制位处理,分类讨论:

  1. 当前\(x\)\(k\)的二进制位为1,那么跳到\(trie[p][0]\)
  2. 当前\(x\)\(k\)的二进制位为0,那么只要让该二进制位为1就能使异或值大于\(k\),然后累计答案再跳到\(trie[p][0]\)
  3. 当前\(x\)的二进制位为0,\(k\)的二进制位为1,那么跳到\(trie[p][1]\)
  4. 当前\(x\)的二进制位为1,\(k\)的二进制位为0,那么只要让该二进制位为0就能使异或值大于\(k\),然后累计答案再跳到\(trie[p][1]\)

代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=1000011;
int a[N],n,m; long long ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
struct Trie{
	int trie[N*30][2],cnt[N*30],tot;
	inline void Clear(){
		trie[1][0]=trie[1][1]=cnt[1]=0,tot=1;
	}
	inline void Insert(int x){
		rr int p=1;
		for (rr int i=29;~i;--i){
			rr int z=(x>>i)&1;
			if (!trie[p][z]){
				trie[p][z]=++tot,cnt[tot]=0,
				trie[tot][0]=trie[tot][1]=0;
			}
			++cnt[p],p=trie[p][z];
		}
		++cnt[p];
	}
	inline signed query(int x){
		rr int p=1,ans=0;
		for (rr int i=29;~i;--i){
			if (!p) return ans;
			if ((x>>i)&1){
				if ((m>>i)&1) p=trie[p][0];
				    else ans+=cnt[trie[p][0]],p=trie[p][1];
			}else{
				if ((m>>i)&1) p=trie[p][1];
				    else ans+=cnt[trie[p][1]],p=trie[p][0];
			}
		}
		return ans+cnt[p];
	}
}trie;
signed main(){
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout); 
	for (rr int T=iut();T;--T){
		trie.Clear(),trie.Insert(0),
		n=iut(),m=iut(),ans=0;
		for (rr int i=1,x=0;i<=n;++i){
			x^=iut(),
			ans+=trie.query(x),
			trie.Insert(x);
		}
		printf("%lld\n",ans);
	}
	return 0;
} 
posted @ 2020-11-02 15:45  lemondinosaur  阅读(78)  评论(0编辑  收藏  举报