考虑如下定义的无限二叉树。
对于标记为v的节点,让其左子节点表示为2 * v,其右子节点表示为2 * v + 1。树的根标记为1。
对于给定的n个范围[a_1,b_1],[a_2,b_2],... [a_n,b_n],对于所有i,(a_i< = b_i),每个范围[a_i,b_i]表示a所有整数的集合不小于a_i且不大于b_i。例如,[5,9]代表集合{5,6,7,8,9}。
对于某个整数T,让S代表所有i到n的并集[a_i,b_i]。 我需要找到S中元素x,y的唯一对的数量(不论顺序),使得lca(x,y)= T
(维基百科对两个节点的LCA有很好的解释。)
例如,输入:
A = {2, 12, 11}
B = {3, 13, 12}
T = 1
输出应为6.(范围为[2,3],[12,13]和[11,12],它们的并集是{2,3,11,12,13}。在所有20对可能的对中,其中正好有6对((2,3),(2,13),(3,11),(3,12),(11,13)和(12,13)) LCA的1。)
输入:
A = {1,7}
B = {2,15}
T = 3
输出应为6.(给定范围为[1,2]和[7,15],它们的并集是{1,2,7,8,9,10,11,12,13}在110对可能的对中,正好有6对((7,12),(7,13),(12,14),(12,15),(13,14)和(13, 15))的LCA为3.)
答案 0 :(得分:1)
嗯,使用这种递归方法计算符号中两个节点的LCA非常简单:
int lca(int a, int b) {
if(a == b) return a;
if(a < b) return lca(b, a);
return lca(a/2, b);
}
现在要找到集合的联合,我们首先需要能够找到特定范围所代表的集合。让我们为此介绍一种工厂方法:
Set<Integer> rangeSet(int a, int b){
Set<Integer> result = new HashSet<Integer>(b-a);
for(int n = a; n <= b; n++) result.add(n);
return result;
}
这将返回包含范围中包含的所有整数的Set<Integer>
。
要找到这些集合的并集,只需将addAll
个元素集合到一个集合中:
Set<Integer> unionSet(Set<Integer> ... sets){
Set<Integer> result = new HashSet<Integer>();
for(Set<Integer> s: sets)
result.addAll(s);
return result;
}
现在,我们需要遍历集合中所有可能的对:
pairLcaCount(int t, Set<Integer> nodes){
int result = 0;
for(int x: nodes)
for(int y: nodes)
if(x > y && lca(x,y) == t) result++;
return result;
}
其他一切只是胶合逻辑,从输入要求转换为此处所需的方法。例如,像:
Set<Integer> unionSetFromBoundsLists(int[] a, int[] b){
Set<Integer> [] ranges = new Set<Integer>[a.length];
for(int idx = 0; idx < ranges.length; idx++)
ranges[idx] = rangeSet(a[idx], b[idx]);
return unionSet(ranges);
}