題目的論述很簡單,就是要找出一個矩形區塊中,樹的數量 >= T  且最少的樹數

 

我原本的想法是,記錄區塊中每一點,其左下方包含的樹數

但這不實際,因為最多有 64000 * 64000 個點,且每個點至少 4 byte ( 假設我們用 int 存 )

則所需空間大約為 64k * 64k * 8 = 幾萬 M  ( 河河河)

 

google 了別人的想法後,發現 BIT 這個神奇的東西

https://blog.csdn.net/Yaokai_AssultMaster/article/details/79492190

 

這裡,我假設你已經懂 BIT 了

我們的想法就是先按樹的 x 座標排序

並依序作為矩形區塊的頂點 (可能是左下,也可能是左上,詳情往下看, 另外,方便起見,我們把它稱作基準點)

 接著將後面的樹加入 (  後面 : x 座標 >= 該點 )

並將加入的點作為矩形區塊的頂點 ( 可能是右上,也可能是右下)

再計算此矩形區塊中的樹數

 

怎麼計算呢?

這裡我們把 y 座標視為原數組的 index,value 部份則是該 y 座標有幾棵樹

每加入一棵樹就更新一次

並計算該矩形的區段和 (也就是基準點的 y 座標,與新加入點的 y 座標的區段)

這個數也代表著此矩形區域的樹數

 

圖示

a533.png

以上圖來說,我們以紅點為基準點,並加入後方的點 ( 紫點、藍點 若干個綠點)

我們先來說說紫點 ,紫點加入時,藍點與右方的綠點皆尚未加入,真正加入的只有紫點左方與紅點右方的綠點

而我們要找的矩形區塊,即為紫色部份

可以發現,紫色區塊皆在紅點上方

因此,整個 BIT 只有考慮紅點上方的部份

那可想而知,每一個基準點應該都要有兩個 BIT ( 分別存上面及下面 )

事實上,紫點的 BIT 區段和直接算就行了

但藍點就必須特別注意

因為籃點 BIT 的區段和是藍色區塊下方

所以藍色矩形的樹數,必須用已加入的樹 (紅點下方)  去扣,才能得到

 

ps:

如果想只用一個 BIT 也是可以,但區段和要算兩次 ( 基準點 和 新加入的點 )

此外

如果基準點在上方,則區段和分別為   sum( 0 , 基準點 y 座標 ) - sum( 0 , 新加入的點的 y 座標 - 1 )

否則為  sum( 0 , 新加入的點的 y 座標 )  - sum( 0 , 基準點 y 座標 - 1 )

 

以下程式碼修改自 台長大大

 

#include <cstdio>
#include <cstdlib>

constexpr int MAX_Y = 64001;


struct tree{
    unsigned short x, y;
};

int cmp(const void *_a, const void *_b)
{
    tree *ta = (tree *)_a;
    tree *tb = (tree *)_b;
    return ta->x - tb->x;
}

int main() {
    int tree_num, query_num;
    while(scanf("%d %d", &tree_num, &query_num) == 2) {
        
        tree T[tree_num];

        int max_y = 0;
        for(int i = 0; i < tree_num; i+=1) {
            scanf("%hu %hu", &T[i].x, &T[i].y);
            T[i].x += 1; T[i].y += 1;
            if(T[i].y > max_y) max_y = T[i].y;
        }

        qsort(T, tree_num, sizeof(tree), cmp);

        int ans = 0xfffffff;
        for(int i = 0; i < tree_num; i+=1) {
            short upperBIT[MAX_Y] = {}, lowerBIT[MAX_Y] = {};
            int upper_tree_cnt = 0, lower_tree_cnt = 0;
            for(int j = i; j < tree_num; j++) {
                if(T[j].y >= T[i].y) {
                    upper_tree_cnt += 1;

                    int idx = T[j].y;
                    while(idx <= max_y) {
                        upperBIT[idx]++;
                        idx += idx&(-idx);
                    }

                    if(upper_tree_cnt >= query_num) {
                        int sum = 0;
                        idx = T[j].y;
                        while(idx) {
                            sum += upperBIT[idx];
                            idx -= idx&(-idx);
                        }
                        if(sum >= query_num && sum < ans)
                            ans = sum;
                    }
                } else {
                    lower_tree_cnt+=1;

                    int idx = T[j].y;
                    while(idx <= T[i].y) {
                        lowerBIT[idx]++;
                        idx += idx&(-idx);
                    }

                    if(lower_tree_cnt >= query_num) {
                        int sum = 0;
                        idx = T[j].y;
                        while(idx) {
                            sum += lowerBIT[idx];
                            idx -= idx&(-idx);
                        }
                        sum = lower_tree_cnt - sum;
                        if(sum >= query_num && sum < ans)
                            ans = sum;
                    }
                }
            }

            if(ans == query_num)
                break;
        }

        printf("%d\n", ans);
    }
    return 0;
}

arrow
arrow
    全站熱搜

    大神(偽) 發表在 痞客邦 留言(0) 人氣()