1. 程式人生 > 其它 >DSA2021秋 PA1 範圍查詢Range 解題思路及程式碼

DSA2021秋 PA1 範圍查詢Range 解題思路及程式碼

解題思路

本題有兩個解法
正常(課程期望)解法:將數軸座標排序後,二分查詢到兩個點,下標相減即可
字首和:將座標直接作為陣列下標,值為[0,i]範圍內存在點的個數。查詢時直接取出邊界點對應的值相減即可

正常解法

  1. 這裡使用快排將座標排序
  2. 二分查詢到對應的點,並將下標相減
#include <cstdio>
#include <cctype>
#define MAXN 500000
int axis[MAXN];

template <typename T>
inline T readstdin()                //加快IO的讀函式
{
    T input = 0;    short sign = 1;    char ch = 0;
    while (!isdigit(ch))
    {
        if(ch == '-')
            sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        input = (input << 3) + (input << 1) + (ch - '0');
        ch = getchar();
    }
    return input * sign;
}
int binSearch( int n, int e)        //二分查詢到[0,n]範圍內的,不大於e的最大值
{
    int lo = 0, hi = n;
    while (lo<hi)
    {
        int mi = (lo+hi)>>1;
        (e<axis[mi]) ? hi=mi : lo = mi + 1;
    }
    return --lo;
}
int getpartition(int lo,int hi){    //用於快排
    int mi=axis[lo];
    while(lo<hi)
    {
        while(lo<hi&&mi<=axis[hi])
            hi--;
        axis[lo]=axis[hi];
        while(lo<hi&&axis[lo]<=mi)
            lo++;
        axis[hi]=axis[lo];
    }
    axis[lo]=mi;
    return lo;
}

void quicksort(int lo,int hi){      //快排
    if(lo<hi)
    {
        int mi=getpartition(lo,hi);
        quicksort(lo,mi-1);
        quicksort(mi+1,hi);
    }
}
int main(int argc, char const *argv[])
{
    int n = readstdin<int>();
    int m = readstdin<int>();

    for (int i = 0; i < n; i++)
    {
        axis[i] = readstdin<int>();
    }
    quicksort(0,n-1);
    int a, b;
    for (int i = 0; i < m; i++)
    {
        a = readstdin<int>()-1;
        b = readstdin<int>();
        int t1 = binSearch(n,a);
        int t2 = binSearch(n,b);
        printf("%d\n",t2-t1);
    }
    return 0;
}

字首和

  1. 初始化:將每個輸入的數軸座標直接作為陣列的下標,若該處有點,則值置為1,否則為0
  2. 生成字首和:axis[i] = axis[i-1] 即每個點的值為[0,i]中有效點的個數
  3. 計算:接收輸入後直接取出對應下標的值相減即可
#include <cstdio>
#include <cctype>
#define MAXN 10000001
int axis[MAXN] = {0};

template <typename T>
inline T readstdin()
{
    T input = 0;    short sign = 1;    char ch = 0;
    while (!isdigit(ch))
    {
        if(ch == '-')
            sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        input = (input << 3) + (input << 1) + (ch - '0');
        ch = getchar();
    }
    return input * sign;
}

int main(int argc, char const *argv[])
{
    int n = readstdin<int>();
    int m = readstdin<int>();
    for (int i = 0; i < n; i++)             //初始化
        axis[readstdin<int>()] = 1;
    for (int i = 1; i < MAXN; i++)          //生成字首和,即統計[0,i]的點總數
        axis[i] += axis[i-1];
    for (int i = 0; i < m; i++)
    {
        int a = readstdin<int>() - 1;
        int b = readstdin<int>();
        printf("%d\n",axis[b]-axis[a]);
    }
    return 0;
}

踩坑

  1. 最開始題目描述裡給的示例讓我一度以為座標是按序輸入的
  2. 即使二分查詢到了對應的下標,相減時也要仔細考慮各種情況並做出修補(+1-1之類的)
  3. 字首和長知識了