超级码力初赛-第二场-第二题-区间异或(初识线段树)
当初看到题目一脸懵逼,后来通过博客查看各大神的题解才知道这题需要用到线段树,经典模板题。
题目的意思我还是从别人的代码读懂的。
题目如下截图:
我来为小白讲解一下题目的意思:就是给定一个数组【1,2,3,4,5】,然后给定多个左右区间,一个左右区间用一个1X4数组表示,比如给的一个左右区间是【1,2,3,5】,即其中的左区间为12,右区间为35,左区间表示num数组中的第一个数到第二个数,即【1,2】,右区间同理,即【3,4,5】。
题目所说的区间区间对和就是左区间最大值2加上右区间最小值3,即 2+3 = 5。
把所有的和异或起来就能得到答案。
弄懂题目后,使用线段树就能够比较轻松地解题了,还要注意题目第一个值下标为1,即num[0]没有值。
解答:
//线段树的结点
class Node{
public int l; //区间最左边下标
public int r; //区间最右边下标
public int max; //区间最大值
public int min; //区间最小值
public Node left_node; //区间左节点
public Node right_node; //区间右节点
}
//num从下标1开始。
public int Intervalxor(int[] num, List<List<Integer>> ask) {
Node root = new Node();
//构建线段树
build_tree(num,1,num.length,root);
int result = 0;
for(List<Integer> list : ask) {
//结果异或左区间最大值加右区间最小值
result ^= getMax(root,list.get(0),list.get(1)) + getMin(root,list.get(2),list.get(3));
}
return result;
}
//递归构建线段树
private void build_tree(int[] num, int l, int r,Node k) {
k.l = l;
k.r = r;
if(l == r) {
k.max = num[l - 1];
k.min = num[l - 1];
return;
}
k.left_node = new Node();
k.right_node = new Node();
int mid = (l + r) >> 1;
build_tree(num,l,mid,k.left_node);
build_tree(num,mid + 1,r,k.right_node);
k.max = Math.max(k.left_node.max, k.right_node.max);
k.min = Math.min(k.left_node.min, k.right_node.min);
}
//当区间只有一个元素时,返回该元素,否则返回其左区间和右区间中元素的最大值的较大值
private int getMax(Node k,int l,int r) {
if(k.l == l && k.r == r) {
return k.max;
}
int mid = (k.l + k.r) >> 1;
if(r <= mid)
return getMax(k.left_node,l,r);
else if(l > mid)
return getMax(k.right_node,l,r);
else
return Math.max(getMax(k.left_node,l,mid), getMax(k.right_node,mid + 1,r));
}
//当区间只有一个元素时,返回该元素,否则返回其左区间和右区间中元素的最小值的较小值
private int getMin(Node k,int l,int r) {
if(k.l == l && k.r == r) {
return k.min;
}
int mid = (k.l + k.r) >> 1;
if(r <= mid)
return getMin(k.left_node,l,r);
else if(l > mid)
return getMin(k.right_node,l,r);
else
return Math.min(getMin(k.left_node,l,mid), getMin(k.right_node,mid + 1,r));
}
_node,l,r);
else
return Math.min(getMin(k.left_node,l,mid), getMin(k.right_node,mid + 1,r));
}