zoukankan      html  css  js  c++  java
  • 异或(数据字典)

    题目描述

    给定整数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 }
  • 相关阅读:
    Struts2框架
    读者写者问题
    哲学家就餐问题
    理解中断
    理解处理机调度
    理解死锁
    理解进程
    Linux CentOS 6.7 挂载U盘
    家庭-养老院模型理解IOC和DI
    Bash基础
  • 原文地址:https://www.cnblogs.com/the-wang/p/8981421.html
Copyright © 2011-2022 走看看