宇宙人浇花(异或前缀和+trie+思维,最大异或对问题)

简介: 宇宙人浇花(异或前缀和+trie+思维,最大异或对问题)

题目传送门

1.题目大意:

a序列的n个数异或和为:a 1 a1a1 xor a 2 a2a2 xor a 3 a3a3 xor a 4 a4a4…xor a n anan

可以对一段连续的a l . . a r al..aral..ar加一,操作一次或者不操作,求操作之后的n个数的异或和

2.分析题目:

最后一定是前面一部分不加一,中间一部分加一,后面一部分不加一,注意每部分可能长度为0

维护两个前缀数组:

1.b [ i ] 维 护 前 i 个 a [ i ] + 1 的 异 或 和 b[i]维护前i个a[i]+1的异或和b[i]ia[i]+1

2.a [ i ] 维 护 前 i 个 a [ i ] 的 异 或 和 a[i]维护前i个a[i]的异或和a[i]ia[i]

3.再用字典树维护a[n] ^ a[i] ^ b[i]

为什么要字典树维护a[n]^a[i] ^b[i]呢?

想想,首先异或运算是满足交换律和结合律的

因为a [ i ] a[i]a[i]维护的是原a数组的前缀异或和,a[i]^a[n]剩下的是原a数组的,a[i+1] ^ a[i+2]a[n]

b[i] ^ a[i] ^ a[n]为 原a数组的前i个(a[i]+1)异或和再异或原 数组的后面的n-i个a[i]的异或和

(a[1]+1) ^ (a[2]+1) ^(a[3]+1) ^ … ^(a[i]+1) ^a[i+1] ^a[i+2] ^… ^ a[n]

遍历一遍将所有的a[i]^a[n] ^b[i]放trie上面

4.再用a[i]^b[i]去查询trie上的最大异或对

为什么是a[i]^b[i]去查询呢?

因为答案一定是某一个 a[x] ^ b[x] ^ (a[n] ^ a[i] ^ b[i])

简单证明一下:

通过3已知,(a[n] ^ a[i] ^ b[i])为:

(a[1]+1) ^ (a[2]+1) ^(a[3]+1) ^ … ^(a[i]+1) ^a[i+1] ^a[i+2] ^… ^ a[n]

(这里的a数组是没有前缀处理的原a数组)

此时a[n] ^ a[i] ^ b[i]再异或b[x]^a[x]

就会将索引为1-x的异或变成0((a[i]+1)^(a[i]+1)),再将索引1-x的异或和变成a[1] ^ a[2] a[x]

整个a[x] ^ b[x] ^ (a[n] ^ a[i] ^ b[i])变成:

a[1] ^ a[2] ^ …^ a[x] ^ (a[x+1]+1) ^ (a[x+1]+1) ^ (a[x+2]+1) (a[x+k]+1) ^ a[x+k+1] ^ …a[n]

分成开始说的三段!

接下来就是求最大异或对了!

代码:

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
# define rep(i,be,en) for(int i=be;i<=en;i++)
# define pre(i,be,en) for(int i=be;i>=en;i--)
typedef pair<int, int> PII;
#define ll long long
#define endl "\n"
#define LOCAL
#define pb push_back
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
inline int read()
{
  int X = 0;
  bool flag = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') flag = 0;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    X = (X << 1) + (X << 3) + ch - '0';
    ch = getchar();
  }
  if (flag) return X;
  return ~(X - 1);
}
inline void write(int X)
{
  if (X < 0) {
    X = ~(X - 1);
    putchar('-');
  }
  if (X > 9) write(X / 10);
  putchar(X % 10 + '0');
}
int son[N][3];
int n;
int idx, a[N], b[N];
void insert(int x)
{
  int p = 0, u;
  for (int i = 31; i >= 0; i--)
  {
    u = (x >> i) & 1;
    if (!son[p][u]) son[p][u] = ++idx;
    p = son[p][u];
  }
}
int query(int x)
{
  int p = 0, u;
  int res = 0;
  for (int i = 31; i >= 0; i--)
  {
    u = (x >> i) & 1;
    if (son[p][u ^ 1]) res += 1 << i, p = son[p][u ^ 1];
    else
      p = son[p][u];
  }
  return res;
}
void solve()
{
  n = read();
  rep(i, 1, n)
  {
    a[i] = read();
    b[i] = a[i] + 1;
  }
  for (int i = 1; i <= n; i++)
  {
    a[i] ^= a[i - 1];
    b[i] ^= b[i - 1];
  }
  for (int i = 0; i <= n; i++)
  {
    int x = a[n] ^ a[i] ^ b[i];
    insert(x);
  }
  int ans = 0;
  for (int i = 0; i <= n; i++)
    ans = max(ans, query(b[i] ^ a[i]));
  cout << ans << "\n";
}
int main() {
  ios::sync_with_stdio;
  cin.tie(0);
  cout.tie(0);
  //#ifdef LOCAL
  //freopen("data.in.txt","r",stdin);
  //freopen("data.out.txt","w",stdout);
  //#endif
  int T = 1;
  //scanf("%d",&T);
  while (T--)
  {
    solve();
  }
  return 0;
}

撰文不易,点个赞再走吧!

目录
相关文章
|
7月前
|
算法
算法思想总结:位运算
算法思想总结:位运算
|
7月前
|
测试技术 Windows
【动态规划】【位运算】1787. 使所有区间的异或结果为零
【动态规划】【位运算】1787. 使所有区间的异或结果为零
|
7月前
|
算法 测试技术 C++
【数学归纳法】【位运算】【异或】3068最大节点价值之和
【数学归纳法】【位运算】【异或】3068最大节点价值之和
|
7月前
|
测试技术
【数学】【记忆化搜索 】【动态规划】964. 表示数字的最少运算符
【数学】【记忆化搜索 】【动态规划】964. 表示数字的最少运算符
|
存储 算法
【每日挠头算法题】Leetcode 989. 数组形式的整数加法 —— 高精度加法解法
【每日挠头算法题】Leetcode 989. 数组形式的整数加法 —— 高精度加法解法
145 0
【每日挠头算法题】Leetcode 989. 数组形式的整数加法 —— 高精度加法解法
|
算法 C语言 容器
力扣260 - 只出现一次的数字||| 【哈希映射、异或位运算+分治思想】
对应力扣260.只出现一次的数字|||,包含哈希映射和异或位运算+分治思想两种解法,超详细步骤讲解
160 0
力扣260 - 只出现一次的数字||| 【哈希映射、异或位运算+分治思想】
|
算法
算法:next数组的求法详解
算法:next数组的求法详解
867 0
算法:next数组的求法详解
|
算法 Java 容器
学前知识准备(递归、取模与取余)
程序调用自身的编程技巧称为递归( recursion)。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
142 0