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 }

 

posted @ 2021-01-02 19:41  acmloser  阅读(180)  评论(0编辑  收藏  举报