夏季每日一题打卡day1 —— AcWing 3485. 最大异或和
Posted Johnny*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了夏季每日一题打卡day1 —— AcWing 3485. 最大异或和相关的知识,希望对你有一定的参考价值。
【题目描述】
解法一
【思路】
异或的一些性质:
a ^ 0 = a
a ^ a = 0
类比前缀和知识:
对于原序列: 0 a1 a2 a3 a4 a5 a6
使用s数组存储上述序列的异或得到: 0 a1 a1^a2 a1^a2^a3 a1^a2^a3^a4 ……
则 计算a2^a3^a4 = (a1^a2^a3^a4) ^ (a1)
= s[4] ^s[1]
异或多余那一段本身,可以消除多余那一段的影响
过 10/13 的数据
import java.util.Scanner;
public class Main{
static int N = 100010;
static int s[] = new int[N]; // 异或数组
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
int n = reader.nextInt(), m = reader.nextInt();
for(int i = 1; i <= n; i ++){
int x = reader.nextInt();
s[i] = s[i - 1] ^ x;
}
int max = 0;
//枚举区间长度
for(int len = 0; len <= m; len ++){
//子数组 [l: r]
for(int l = 1; l + len - 1 <= n; l ++){
int r = l + len - 1;
int res = s[r] ^ s[l - 1];
if( res > max) max = res;
}
}
System.out.println(max);
}
}
【思路】
解法二
二进制的每一位对应Trie的一条边
Trie 区间和
使用Trie存储异或结果二进制表示的01串
要想使得异或结果尽可能地大,根据按位异或的特点(相同为0,相异位1),每一位尽可能的选择与要异或的数对应位相反的数。那么对于每一个s[i], 从高位到低位应该尽可能地选择与s[i]相异的01串 s [ j ] (i - j < m)。
每次筛掉与该位相同的集合,Trie从上往下走的过程就对应不断筛掉一半区间的过程。时间复杂度:32log(32*N)
import java.util.Scanner;
public class Main{
static int N = 100010, M = 100010 *31;
//son 第一维表示节点编号 第二维表示0(右)、1(左)子节点
//存储的p节点的0或1儿子的节点编号
static int son[][] = new int[M][2]; //有N个数 每个数是一个31位的01串 一共有M个节点
static int cnt[] = new int[M]; //以编号为i为根节点的01串(数)的个数
static int idx;
static int s[] = new int[N]; // 异或数组
public static void insert(int x, int v){
int p = 0;
//从高位往低位枚举
for(int i = 30; i >=0; i --){
int u = x >> i & 1;
if( son[p][u] == 0 ) son[p][u] = ++idx; //p不存在u这个子节点则new一个
//p走到儿子节点上
p = son[p][u];
cnt[p] += v;
}
}
public static int query(int x){
int p = 0, res = 0;
for(int i = 30; i >= 0; i --){
int u = x >> i & 1;
//相异的数存在
if( cnt[ son[p][1 - u] ] != 0 ){
p = son[p][1 - u];
res = res * 2 + 1; //这一位是1
}else{
p = son[p][u]; //走到子节点
res = res * 2 + 0; //这一位是0
}
}
return res;
}
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
int n = reader.nextInt(), m = reader.nextInt();
for(int i = 1; i <= n; i ++){
int x = reader.nextInt();
s[i] = s[i - 1] ^ x;
}
int res = 0;
insert(s[0], 1);
for(int i = 1; i <= n; i ++){
//要求异或和连续数组是:s[max(i-m,0)]~s[i]
//也就是S[j]范围是s[ i - m - 1 ,i- 1]
//滑出窗口的数要移除:
if( i > m ) insert(s[i - m - 1], - 1);
res = Math.max(res, query(s[i]));
insert(s[i], 1);
}
System.out.println(res);
}
}
143. 最大异或对
【题目描述】
【思路】
如果要求最大的异或对是哪两个,可以用如下
import java.io.*;
import java.lang.Math;
class Main{
static int N = 100010, M = 100010 *31;
static int idx;
static int son[][] = new int[M][2];
static int a[] = new int[N];
public static void insert(int x){
int p = 0;
for(int i = 30; i >= 0; i --){
int u = x >> i & 1;
if( son[p][u] == 0 ) son[p][u] = ++ idx;
p = son[p][u];
}
}
public static int query(int x){
int res = 0, p = 0;
for(int i = 30; i >= 0; i --){
int u = x >> i & 1;
if( son[p][1 - u] == 0){//相异的数不存在
res = res * 2 + u ;
p = son[p][u];
}
else {
res = res * 2 + 1 - u;//相异的数存在
p = son[p][1 - u];
}
}
return res;
}
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bf.readLine());
String s[] = bf.readLine().split(" ");
for(int i = 0; i < n; i++) a[i] = Integer.parseInt(s[i]);
int res = 0; // 最小为0(相同的数异或为0)
for(int i = 0; i < n; i++){
insert(a[i]);//先往tire中插入数据 避免空集特判
}
for(int i = 0; i < n; i++){
int t = query(a[i]);
res = Math.max(res, a[i] ^ t);
}
System.out.println(res);
}
}
以上是关于夏季每日一题打卡day1 —— AcWing 3485. 最大异或和的主要内容,如果未能解决你的问题,请参考以下文章
夏季每日一题打卡day4 —— AcWing 3502. 不同路径数
夏季每日一题打卡day5——AcWing 3489. 星期几