题目
代码
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 3100000+10;
int n,m;
int trie[N][2],cnt[N];//cnt为以该节点为根节点的树里有多少个叶子节点
int idx;
int s[N];//存放前缀异或和
void insert(int x, int v)//插入时v为1,删除时v为-1
{
int p=0;
for(int i=30; i>=0; --i)//a最大值为2的31次方减1,把a表示为二进制,只有31位,右移0~30位再&1就可以得到a的二进制的每一位,从i=30开始是为了从a的高位开始录入字典树
{
int u=x>>i&1;//右移i位,&1是因为int有32位,&1去掉前31位
if(!trie[p][u]) trie[p][u]=++idx;
p=trie[p][u];
cnt[p]+=v;
}
}
int query(int x) //查询trie中与x异或和的最大值
{
int res=0, p=0;//异或和
for(int i=30; i>=0; --i)
{
int u=x>>i&1;
if(cnt[trie[p][!u]]) p=trie[p][!u], res=res*2+1;
else p=trie[p][u], res*=2;
}
return res;
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; ++i)
{
int x;
cin>>x;
s[i]=s[i-1]^x;//前缀和
}
int res=0;
insert(s[0],1);//空数组,结果为0
for(int i=1; i<=n; ++i)
{
if(i-m-1>=0) insert(s[i-m-1],-1);//滑动窗口,向右滑动,删除上一次的左端起点。这里的左端起点是L-1,上一次的左端起点是L-2,L>=R-m+1,则删除的是R-m-1
res = max(res, query(s[i]));
insert(s[i], 1);
}
cout<<res<<endl;
return 0;
}
/*
先计算出前缀异或和,m长度的异或和为s[r]^s[l-1],其中r-l+1=m
利用滑动窗口和字典树,窗口移动到哪一段,字典树中只保存s[l-1]到s[r]的值,不满足可以计算出一段小于等于m长度异或和的前缀异或和都被删除了
确定右端点,用query函数,以右端点确定的s[r]与字典树中的前缀异或和进行计算
u=x>>i&1,就是从s[r]的高位开始取出每一位和字典树中的节点进行异或
想要异或和最大,我们知道异或时同为0异为1,因此我们找到一个前缀异或和,保证它的每一个与s[r]对应的位跟s[r]不同,也就是和u不同即可
因此我们去找trie[p][!u]是否存在,如果存在,那么当前结果res左移一位再加上异或和的结果1,也就是res=res*2+1
如果trie[p][!u]不存在,那么就只有trie[p][u],这一位跟s[r]的对应位是相同的,异或结果为0,直接把res左移一位即可
*/
标签:insert,最大,int,res,算法,trie,异或,include
From: https://www.cnblogs.com/HD0117/p/17204483.html