📜  须藤铺位|范围查询

📅  最后修改于: 2021-05-25 02:22:51             🧑  作者: Mango

给定Q个查询,每个查询由两个整数L和R组成,任务是查找L和R之间的总数(包括两个端点),它们的二进制表示形式中最多有三个设置位。

例子

Input : Q = 2
        L = 3, R = 7
        L = 10, R = 16
Output : 5
         6
For the first query, valid numbers are 3, 4, 5, 6, and 7.
For the second query, valid numbers are 10, 11, 12, 13, 14 and 16.

先决条件:位操作和二进制搜索

方法1(简单) :天真的方法是遍历L和R之间的所有数字,并在每个数字中找到设置的位数。如果一个数字的设置位数不超过3个,则递增计数器变量。返回答案作为计数器。注意:由于数字L和R可能具有较大的值(最多10 18 ),因此此方法效率很低。

方法2(有效) :这里需要的一种有效方法是预计算。由于L和R的值在[0,10 18 ]范围内(包括两端值),因此它们的二进制表示形式最多可以具有60位。现在,由于有效数字是具有最多3个置位的数字,因此可以通过生成60个位数少于或等于3个置位的所有位序列来找到它们。这可以通过为(0,60)中的所有i,j,k固定ithjthkth位来完成。一旦所有有效数字按排序顺序生成,请应用二进制搜索来查找位于给定范围内的那些数字的计数。

下面是上述方法的实现。

C++
// CPP program to find the numbers
// having atmost 3 set bits within
// a given range
#include 
  
using namespace std;
  
#define LL long long int
  
// This function prints the required answer for each query
void answerQueries(LL Q, vector > query)
{
    // Set of Numbers having at most 3 set bits
    // arranged in non-descending order
    set s;
  
    // 0 set bits
    s.insert(0);
  
    // Iterate over all possible combinations of
    // i, j and k for 60 bits
    for (int i = 0; i <= 60; i++) {
        for (int j = i; j <= 60; j++) {
            for (int k = j; k <= 60; k++) {
                // 1 set bit
                if (j == i && i == k)
                    s.insert(1LL << i);
  
                // 2 set bits
                else if (j == k && i != j) {
                    LL x = (1LL << i) + (1LL << j);
                    s.insert(x);
                }
                else if (i == j && i != k) {
                    LL x = (1LL << i) + (1LL << k);
                    s.insert(x);
                }
                else if (i == k && i != j) {
                    LL x = (1LL << k) + (1LL << j);
                    s.insert(x);
                }
  
                // 3 set bits
                else {
                    LL x = (1LL << i) + (1LL << j) + (1LL << k);
                    s.insert(x);
                }
            }
        }
    }
    vector validNumbers;
    for (auto val : s)
        validNumbers.push_back(val);
  
    // Answer Queries by applying binary search
    for (int i = 0; i < Q; i++) {
        LL L = query[i].first;
        LL R = query[i].second;
  
        // Swap both the numbers if L is greater than R
        if (R < L)
            swap(L, R);
        if (L == 0)
            cout << (upper_bound(validNumbers.begin(), validNumbers.end(),
                    R) - validNumbers.begin()) << endl;
        else
            cout << (upper_bound(validNumbers.begin(), validNumbers.end(),
                   R) - upper_bound(validNumbers.begin(), validNumbers.end(),
                   L - 1)) << endl;
    }
}
  
// Driver Code
int main()
{
    // Number of Queries
    int Q = 2;
    vector > query(Q);
    query[0].first = 3;
    query[0].second = 7;
    query[1].first = 10;
    query[1].second = 16;
  
    answerQueries(Q, query);
    return 0;
}


Java
// Java program to find the numbers
// having atmost 3 set bits within
// a given range
import java.util.*;
import java.io.*;
  
public class RangeQueries {
  
    //Class to store the L and R range of a query
    static class Query {
        long L;
        long R;
    }
  
    //It returns index of first element which is grater than searched value
     //If searched element is bigger than any array element function
     // returns first index after last element.
    public static int upperBound(ArrayList validNumbers,
                                    Long value)
    {
        int low = 0;
        int high = validNumbers.size()-1;
  
        while(low < high){
            int mid = (low + high)/2;
            if(value >= validNumbers.get(mid)){
                low = mid+1;
            } else {
                high = mid;
            }
        }
        return low;
    }
  
    public static void answerQueries(ArrayList queries){
        // Set of Numbers having at most 3 set bits
        // arranged in non-descending order
        Set allNum = new HashSet<>();
  
        //0 Set bits
        allNum.add(0L);
  
        //Iterate over all possible combinations of i, j, k for
        // 60 bits. And add all the numbers with 0, 1 or 2 set bits into
        // the set allNum.
        for(int i=0; i<=60; i++){
            for(int j=0; j<=60; j++){
                for(int k=0; k<=60; k++){
  
                    //For one set bit, check if i, j, k are equal
                    //if yes, then set that bit and add it to the set
                    if(i==j && j==k){
                        allNum.add(1L << i);
                    }
  
                    //For two set bits, two of the three variable i,j,k
                    //will be equal and the third will not be. Set both
                    //the bits where two variabls are equal and the bit 
                    //which is not equal, and add it to the set
                    else if(i==j && j != k){
                        long toAdd = (1L << i) + (1L << k);
                        allNum.add(toAdd);
                    }
                    else if(i==k && k != j){
                        long toAdd = (1L << i) + (1L << j);
                        allNum.add(toAdd);
                    }
                    else if(j==k && k != i){
                        long toAdd = (1L << j) + (1L << i);
                        allNum.add(toAdd);
                    }
  
                    //Setting all the 3 bits
                    else {
                        long toAdd = (1L << i) + (1L << j) + (1L << k);
                        allNum.add(toAdd);
                    }
  
                }
            }
        }
  
        //Adding all the numbers to an array list so that it can be sorted
        ArrayList validNumbers = new ArrayList<>();
        for(Long num: allNum){
            validNumbers.add(num);
        }
  
        Collections.sort(validNumbers);
  
        //Answer queries by applying binary search
        for(int i=0; i queries = new ArrayList<>();
  
        Query q1 = new Query();
        q1.L = 3;
        q1.R = 7;
  
        Query q2 = new Query();
        q2.L = 10;
        q2.R = 16;
  
        queries.add(q1);
        queries.add(q2);
  
        answerQueries(queries);
    }
  
}


时间复杂度:O((最大位数) 3 + Q * logN),其中Q是查询数,N是包含所有有效数的集合的大小。 l有效数字。