异或(数据字典)

题目描述

给定整数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 }

猜你喜欢

转载自www.cnblogs.com/the-wang/p/8981421.html