题目描述
给定整数m以及n各数字A1,A2,..An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果,请求出这些结果中大于m的有多少个。
输入描述:
第一行包含两个整数n,m.
第二行给出n个整数A1,A2,...,An。
数据范围
对于30%的数据,1 <= n, m <= 1000
对于100%的数据,1 <= n, m, Ai <= 10^5
输出描述:
输出仅包括一行,即所求的答案
示例1
输入
3 10 6 5 10
输出
2
1 import java.sql.Time; 2 import java.util.ArrayList; 3 import java.util.List; 4 import java.util.Scanner; 5 6 /** 7 思路: 直接计算肯定是超时的,所以这问题不能使用暴力破解,考虑到从高位到地位,依次进行位运算, 8 * 如果两个数异或结果在某高位为1,而m的对应位为0,则肯定任何这两位异或结果为1的都会比m大。 9 * 由此,考虑使用字典树(TrieTree)从高位到低位建立字典,再使用每个元素依次去字典中查对应 高位异或为1, 10 * 而m为0的数的个数,相加在除以2既是最终的结果;直接贴出代码如下,非原创,欢迎讨论; 11 * 补充:queryTrieTree在搜索的过程中,是从高位往低位搜索,那么,如果有一个数与字典中的数异或结果 12 * 的第k位大于m的第k位,那么该数与对应分支中所有的数异或结果都会大于m, 否则,就要搜索在第k位异或 13 * 相等的情况下,更低位的异或结果。queryTrieTree中四个分支的作用分别如下: 14 1. aDigit=1, mDigit=1时,字典中第k位为0,异或结果为1,需要继续搜索更低位,第k位为1,异或结果为0,小于mDigit,不用理会; 15 2. aDigit=0, mDigit=1时,字典中第k位为1,异或结果为1,需要继续搜索更低位,第k位为0,异或结果为0,小于mDigit,不用理会; 16 3. aDigit=1, mDigit=0时,字典中第k位为0,异或结果为1,与对应分支所有数异或,结果都会大于m,第k位为1,异或结果为0,递归获得结果; 17 4. aDigit=0, mDigit=0时,字典中第k位为1,异或结果为1,与对应分支所有数异或,结果都会大于m,第k位为0,异或结果为0,递归获得结果; 18 * 改进: 19 1.字典树17位即可保证大于100000,移位范围为1~16位,则字典树构建时从16~0即可字典树第一层不占位,实际上是15~-1层有数据,这也是数据中next的用法。 2.queryTrieTree函数需要考虑到index为-1时的返回值。 20 21 时间复杂度:O(n); 空间复杂度O(k),k为常数(trie树的高度),因此可以认为O(1)。 异或 22 * 23 * @author Dell 24 * 25 */ 26 public class Main { 27 private static class TrieTree { 28 TrieTree[] next = new TrieTree[2]; // 放置子节点 29 int count = 1; // 记录通过这条路的 元素的个数 30 } 31 public static void main(String[] args) { 32 33 Scanner sc = new Scanner(System.in); 34 while (sc.hasNext()) { 35 int n = sc.nextInt(); 36 int m = sc.nextInt(); 37 int[] a = new int[n]; 38 for (int i = 0; i < n; i++) { 39 a[i] = sc.nextInt(); 40 } 41 System.out.println(solver(a, m)); 42 } 43 } 44 static private long solver(int[] a, int m) { 45 TrieTree tree = setTrieTree(a); 46 long result = 0; 47 for (int i = 0; i < a.length; i++) { 48 result += queryTrieTree(tree, a[i], m, 31); 49 } 50 51 return result / 2; 52 } 53 static private long queryTrieTree(TrieTree tree, int a, int m, int index/* 用于记录查找的层数 */) { 54 if (tree == null) { 55 return 0; // 退出递归 56 } 57 TrieTree curTree = tree; 58 59 // 循环用于这一层得不到结果时继续向下 查找 60 for (int i = index; i >= 0; i--) { 61 int aDigit = (a >> i) & 1;// i 在index 的基础上继续向下查找 每次都要取位数1 62 int mDigit = (m >> i) & 1; 63 // 由于 字典树 第一层不包含 字符 所以aDigit就直接跟next里的字符 异或反映出来就是本层next里的下标 64 // 所以在取next[0] next[1] 时已经指定了k位的值 65 // 即tree为k层时比较的时k+1位的数据 66 if (aDigit == 1 && mDigit == 1) { 67 if (curTree.next[0] == null) { // 1^0=1 才能使得 a m 相等 暂时还在我们的统计范围内要验证 next[0] 68 return 0; // 遇到null 没有路退出 69 } 70 // 1^1 = 0 a<m不符合条件 退出 71 curTree = curTree.next[0]; 72 } else if (aDigit == 0 && mDigit == 1) {// 0^1=1 才能使得 a m 相等 暂时还在我们的统计范围内要验证 next[1] 73 if (curTree.next[1] == null) { 74 return 0; // 遇到null 没有路退出 75 } 76 // 0^0=0 a<m 不符合条件 退出 77 curTree = curTree.next[1]; 78 } else if (aDigit == 1 && mDigit == 0) { 79 long p = queryTrieTree(curTree.next[1], a, m, i - 1);// 1^1=0 a m相等 继续验证 80 long q = curTree.next[0] == null ? 0 : curTree.next[0].count;// 1^0=1 a>m 直接取结果 81 return p + q; 82 } else if (aDigit == 0 && mDigit == 0) { 83 long p = queryTrieTree(curTree.next[0], a, m, i - 1);// 0^0=0 am 相等 继续验证 84 long q = curTree.next[1] == null ? 0 : curTree.next[1].count;// 0^1=1 a>m直接取结果 85 return p + q; 86 } 87 } 88 89 return 0; 90 } 91 static public TrieTree setTrieTree(int[] a) { 92 TrieTree tree = new TrieTree(); 93 for (int i = 0; i < a.length; i++) { 94 TrieTree curTree = tree; 95 for (int j = 31; j >= 0; j--) { 96 int aDigit = (a[i] >> j) & 1; 97 if (curTree.next[aDigit] == null) { 98 curTree.next[aDigit] = new TrieTree(); 99 } else { 100 curTree.next[aDigit].count++; 101 } 102 103 curTree = curTree.next[aDigit];// 下一层 104 } 105 } 106 return tree; 107 } 108 }