Codechef REBXOR 黑暗爆炸 - 4260
Trie+位运算+前缀和思想+后缀和思想
错误思路:
如果枚举两个端点,结果还是会TLE,就算先预处理每个sum[i]对应异或的最大值,排序,考虑到最坏情况结果时间复杂度还是会到10的10次方,因此此解不可行
正确思路:
本道题要求两个区间内的异或最大值,我们可以用一个数组ans[i]记录1~i的前缀和异或最大值(注意记录的不是sum[i]询问后的异或最大值,因为我们要求的区间不一定是连续的),这样在枚举后缀和异或最大值后,可以将答案转化为ans[i-1]+query(last[i]),我们保证了1~i区间内ans[i]的数值是最大的,那么只需要枚举last[i]就可以求出最大值
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 #define ll long long 6 const int N = 4e5+10; 7 int idx,son[32*N][2],pre[N],last[N],a[N]; 8 ll ans[N];//前缀和+后缀和+Trie 9 void insert_t(int x) 10 { 11 int p = 0; 12 for(int i=31;~i;i--){ 13 int t = x>>i&1; 14 if(!son[p][t]) son[p][t] = ++idx; 15 p = son[p][t]; 16 } 17 } 18 ll query(int x)//异或最大值 19 { 20 int p = 0; 21 ll res = 0; 22 for(int i=31;~i;i--){ 23 int t = x>>i&1; 24 if(son[p][!t]){ 25 res+=1ll<<i; 26 p = son[p][!t]; 27 }else p = son[p][t]; 28 } 29 return res; 30 } 31 int main() 32 { 33 //freopen("in.txt","r",stdin); 34 int n; ll res = 0; 35 scanf("%d",&n); 36 insert_t(0); 37 for(int i=1;i<=n;i++){ 38 scanf("%d",&a[i]); 39 pre[i] = pre[i-1]^a[i];//前缀异或 40 } 41 for(int i=n;i>0;i--) last[i] = last[i+1]^a[i]; 42 for(int i=1;i<=n;i++){ 43 ans[i] = max(ans[i-1],query(pre[i]));//询问与pre[i]异或的最大值 44 insert_t(pre[i]); //ans[i]是前i个数的最大值 45 } 46 memset(son,0,sizeof(son)); idx = 0; 47 insert_t(0); 48 for(int i=n;i>0;i--){ 49 res = max(ans[i-1]+query(last[i]),res); 50 insert_t(last[i]); 51 } 52 printf("%lld\n",res); 53 return 0; 54 }