Data Structures and Algorithms: Topics on Bitwise Operations

Data Structures and Algorithms: Topics on Bitwise Operations

Basics of Bit Operations

  Bitwise operations refer to a series of operations performed directly on binary bits. The commonly used bit operations are as follows:

  • AND (bitwise AND): The operator is &, and its function is to AND the binary bits corresponding to two numbers. Only when the corresponding two bits are 1, the result bit is 1, otherwise the result bit is 0. For example: 13&12=1101&1100=1100=12.
  • OR (bitwise or): The operator is |, the function is to OR the binary bits corresponding to the two numbers. Only when the corresponding two bits are 0, the result bit is 0, otherwise the result bit is 1. For example: 9|10=1001|1010=1011=11.
  • XOR (Bitwise Exclusive OR): The operator is ^, the function is to XOR the binary bits corresponding to the two numbers, if the corresponding two bits are different, the result bit is 1; if the corresponding two bits are the same, the result bit is 0. For example: 13^12=1101^1100=0001=1.
  • Shift operation:
      a. Left shift operation: The operator is <<, which is used to shift the number to the left at the same time in binary representation, fill the lower bits with 0, and discard the higher bits after they exceed the boundary. For example, 2<<1=...00010<<1=...00100=4.
      b. Right shift operation: the operator is >>, which is used to shift the number to the right at the same time in binary representation, fill the high bits with 0, and discard the low bits after they cross the boundary. For example 2>>1=...00010>>1=...00001=1.

  These common functions can be realized by using the above bit operation operations:

  • if((1<<i)&x), to judge whether the i-th bit on the binary of x is 1.
  • x^(1<<i), invert the i-th bit on the binary of x.
  • x|(1<<i), assign the i-th bit of x binary as 1.
  • x>>1, which is equivalent to x/2, which is faster than using the division operator.
  • if(x&1), to judge parity, when x is odd, the lowest bit in binary is 1; when x is even, the lowest bit in binary is 0.
  • State compression, which represents a set of states with a binary number. For example, we perform binary enumeration: for(i=0;i<(1<<4);i++), that is, enumerate from 0000 to 1111, then the jth bit of i is 0, which means that this number is not taken, and the value of i If the jth bit is 1, it means to take this number. State compression is often used in topics such as search/dynamic programming.

bit operation example

Example 1: PIPI's bit operation problem IV

question

insert image description here
insert image description here

problem solving ideas

  For most of the problems of bit operations, we need to master the idea of ​​bitwise considerationWhat is bitwise consideration? That is, instead of considering the number as a whole, convert the number into binary form, and think about the method of solving the problem from each of its digits.
  For example, for this question, the question is to calculate the XOR sum of the i-th number and the previous i-1 numbers, then we need to treat these numbers as binary form, and the i-th number is different from the previous number Or, when the j-th digit of the i-th number is different from the j-th digit of another number, ans needs to add 2 ^ (j - 1). For the first i - 1 numbers, the calculation methods are the same, so we have to consider bit by bit, and calculate the contribution of each bit to the answer ans: 如果第i个数字的第j位为1,那么它对最终答案的贡献就是前i-1个数第j位为0的个数 * 2 ^ (j - 1);如果第i个数字的第j位为0,那么它对最终答案的贡献就是前i-1个数第j位为1的个数 * 2 ^ (j - 1).
  Therefore, we use a JthBitHasOne array when traversing with i. JthBitHasOne[j] means that there are JthBitHasOne[j] with the jth digit of the first i-1 number being 1, and then the jth digit of the first i-1 number is 0. There are i-1-JthBitHasOne[j]. A number that does not exceed 1e9 only needs to traverse to the 29th digit at most. According to the above-mentioned method of calculating contribution by bit, we can optimize the time complexity of O(n^2) on the topic to O(n* log(max(ai))).

  Next, use the example (1,2,3) to understand the method of calculating the contribution by bitwise consideration:
  when i=1, there is no previous number that can be XORed with it, and ans=0 at this time.
  When i=2, the binary form of a2 is 10. The 0th bit of a2 is 0. According to the characteristics of XOR, the same is 0, and the different is 1. Then the 0th bit of the first i-1 numbers is 1, which can contribute 2 ^ 0 to the final answer. The first i-1 numbers are only a1, and the 0th bit is 1, so the contribution to the answer is 1 x 2 ^ 0 = 1. Then ans+=1, at this time ans=1.
  The first bit of a2 is 1. According to the characteristics of XOR, it is 0 if it is the same, and it is 1 if it is not the same. Then the first i-1 number is 0, which can contribute 2 ^ 1 to the final answer. The first i-1 number is only a1, and the first digit is 0, so the contribution to the answer is 1 x 2 ^ 1 = 2. Then ans+=2, at this time ans=3.
  When i=3, the binary form of a3 is 11. The 0th bit of a3 is 1. According to the characteristics of XOR, the same is 0, and the different is 1. Then the 0th bit of the first i-1 numbers is 0, which can contribute 2 ^ 0 to the final answer. The first i-1 number is only a2, and the 0th bit is 0, so the contribution to the answer is 1 x 2 ^ 0 = 1. Then ans+=1, at this time ans=4.
  The first bit of a3 is 1. According to the characteristics of XOR, it is 0 if it is the same, and 1 if it is not the same. Then the first i-1 numbers with 0 in the first bit can contribute 2 ^ 1 to the final answer. The first i-1 number is only a1, and the first digit is 0, so the contribution to the answer is 1x2^1=2. Then ans+=2, at this time ans=6. So the final answer is 6.
insert image description here

the code
import java.util.*;

public class Main {
    
    
    static int[] JthBitHasOne = new int[32];
    static long[] twoPower = new long[32];
    public static void main(String[] args) {
    
    
        long ans = 0;
        int n, i, j;
        long a;
        twoPower[0] = 1;
        for (i = 1; i < 32; i++) {
    
    
            twoPower[i] = twoPower[i - 1] * 2;
        }
        Arrays.fill(JthBitHasOne, 0);
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        for (i = 1; i <= n; i++) {
    
    
            a = scanner.nextLong();
            for (j = 0; j < 32; j++) {
    
    
                if ((a & (1L << j)) != 0) {
    
    
                    ans += (i - 1 - JthBitHasOne[j]) * twoPower[j];
                    JthBitHasOne[j]++;
                } else {
    
    
                    ans += JthBitHasOne[j] * twoPower[j];
                }
            }
        }
        System.out.println(ans);
    }
}

  Points to note are:

  • How to judge whether the jth digit of the i-th number is 1 or 0?
    a & (1L << j)That is, if the result is 1, then the j+1th bit of a is 1, otherwise it is 0

Example 2: PIPI's bit operation problem Ⅲ

question

insert image description here
insert image description here

problem solving ideas

  As in the previous question, we consider bit by bit. The key to solving this problem lies in the understanding of OR operation and XOR operation. N numbers are ORed, for the i-th bit, if there is a number that is 1, according to the nature of the OR operation, the bit is also 1 as a result of the OR, so it will contribute 2^i to the answer; n numbers Perform an XOR operation, for the i-th bit, if there are an odd number of numbers, this bit is 1, according to the nature of the XOR operation, the result of the XOR operation is also 1, so it will contribute 2^i to the answer. In the case of XOR of n numbers, for the i-th bit, only when there are an odd number of 1s, the XOR result of this bit is 1, and the XOR result of this bit is 0 in other cases.
  And because the title requires the maximum value, we might as well be greedy from high to low. We use the variable i to traverse from high to low. For each i, we first find the number of n numbers whose i-th bit is 1. If the i-th bit is 1 and the number is 0, we need to use a change opportunity to make the i-th bit 1 number become 1, so that the results of XOR and OR are both 1, and a total of 2 x 2 ^i's contribution. If the number of 1s in the i-th bit is not 0, the number of 1s is either odd or even. If it is odd, the results of XOR and OR are both 1, and a total of 2 x 2 ^ i contributions are generated. If it is an even number, it can be changed to an odd number with one change opportunity.

the code
import java.util.*;
 
public class Main {
    
    
    static int[] oneNum = new int[65];
    static long[] twoPower = new long[65];
    public static void main(String[] args) {
    
    
        twoPower[0] = 1;
        int i, n, j, maxj = 1;
        for (i = 1; i < 65; i++) {
    
    
            twoPower[i] = twoPower[i - 1] * 2;
        }
        Arrays.fill(oneNum, 0);
        long k, a, ans = 0;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        k = scanner.nextLong();
        for (i = 0; i < n; i++) {
    
    
            a = scanner.nextLong();
            for (j = 0; j < 64; j++) {
    
    
                if ((a & (1L << j)) != 0) {
    
    
                    oneNum[j + 1]++;
                    maxj = Math.max(j + 1, maxj);
                }
            }
        }
        for (j = maxj; j >= 1; j--) {
    
    
                if (oneNum[j] == 0) {
    
    
                    if (k > 0) {
    
    
                        k--;
                        ans += 2 * twoPower[j - 1];
                    }
                }else if ((oneNum[j] & 1) == 0) {
    
    
                    if (k > 0) {
    
    
                        k--;
                        ans += 2 * twoPower[j - 1];
                    } else {
    
    
                        ans += twoPower[j - 1];
                    }
                } else {
    
    
                    ans += 2 * twoPower[j - 1];
                }
        }
        System.out.println(ans);
    }
}

Guess you like

Origin blog.csdn.net/qq_44709990/article/details/123307129