bzoj 4260 Codechef REBXOR——trie树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4260

一段的异或和就是两个前缀的异或和。正反扫两边,用trie树算每个位置为左/右端点时最大异或和,并维护前/后缀max;然后枚举分界线即可。

注意&出来是值,要弄一弄才能变成bool。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=4e5+5,M=30;
int n,a[N],bin[M+5],tot,c[N*M][2],s[N][2],mx,ans;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
void init()
{
  bin[0]=1;for(int i=1;i<=n;i++)bin[i]=bin[i-1]<<1;
}
int calc(int sm)
{
  int cr=0,ret=0;
  for(int i=mx,d;i>=0;i--)
    {
      d=((sm&bin[i])>0);//((__)>0)
      if(c[cr][!d])cr=c[cr][!d],ret|=bin[i];
      else cr=c[cr][d];
    }
  return ret;
}
void insert(int sm)
{
  int cr=0;
  for(int i=mx,d;i>=0;i--)
    {
      d=((sm&bin[i])>0);//
      if(!c[cr][d])c[cr][d]=++tot;
      cr=c[cr][d];
    }
}
int main()
{
  n=rdn(); init();
  for(int i=1;i<=n;i++)a[i]=rdn(),mx=max(mx,a[i]);
  for(int i=30;i>=0;i--)if(mx&bin[i]){mx=i;break;}
  int sm=0; insert(0);//after cal mx!
  for(int i=1;i<=n;i++)
    {
      sm^=a[i]; s[i][0]=max(s[i-1][0],calc(sm));
      insert(sm);
    }
  tot=0;memset(c,0,sizeof c);insert(0);
  sm=0;
  for(int i=n;i;i--)
    {
      sm^=a[i]; s[i][1]=max(s[i+1][1],calc(sm));
      insert(sm);
    }
  for(int i=1;i<n;i++) ans=max(ans,s[i][0]+s[i+1][1]);
  printf("%d\n",ans);
}

 

posted on 2018-10-17 10:46  Narh  阅读(119)  评论(0编辑  收藏  举报

导航