計蒜客 - 棋子等級

計蒜客 棋子等級

假定棋子的等級是左下方的棋子個數,現在給出若干棋子的位置,求不同等級的棋子各有多少個。

輸入格式
第一行一個整數 N(1N100000)N (1\leq N\leq 100000)
接下來 NN 行,一行兩個整數 X,Y(0X,Y<100000)X,Y (0\leq X, Y < 100000),表示座標。
數據保證座標先按 YY 排序,再按 XX 排序。

5
1 1
5 1
7 1
3 3
5 5

輸出格式
NN 行,每行一個整數,從 00N1N−1 等級的棋子數量。

1
2
1
1
0

因爲題目保證了輸入數據的順序,所以這題就變成了裸的樹狀數組。
爲什麼這麼說呢?
當我們遇到一個點 (x, y) 的時候,由於題目保證了先按 Y 排序再按 X 排序,所以在 (x, y) 左下角的點一定都遇到過,之後的點都不可能在 (x, y) 左下角,並且,之前遇到的點都是在 (x, y) 左下角的,沒有在 (x, y) 其他方位的。
這樣一來,如果遇到一個點 (x, y) 的時候,getSum(x) 就會得到i=1xCi\sum_{i=1}^{x}{C_i}的和,即樹狀數組中從 1 開始到 x 的累加值。
可以令每次 change() 的值爲 1,這樣 sum() 的結果就變成了計數,即出現在 x 之前的點的個數,這個個數就是所求的棋子等級。
只需要在對應棋子等級的計數器上加 1。

ans[getSum(x)]++; // 這個棋子左下的棋子個數是 getSum(x),則對應該等級的棋子個數加 1
change(x); // 把棋子放在這個位置

完整代碼如下:

#include <bits/stdc++.h>

using namespace std;

int n = 0;
const int MAX_N = 100007;
int C[MAX_N] = {0};

int lowBit(int x) {
    return x & -x; // return x & (x ^ (x - 1))
}

int getSum(int x) {
    int res = 0;
    while (x != 0) {
        res += C[x];
        x -= lowBit(x);
    }
    return res;
}

void change(int x) {
    while (x <= MAX_N) {
        C[x]++;
        x += lowBit(x);
    }
}

int main() {
    scanf("%d", &n);
    int ans[n];

    for (int i = 0; i < n; i++) {
        ans[i] = 0;
    }

    for (int i = 0; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        x++; // 樹狀數組的下標從 1 開始
        ans[getSum(x)]++; // 這個棋子左下的棋子個數是 getSum(x),則對應該等級的棋子個數加 1
        change(x); // 把棋子放在這個位置
    }

    for (int a: ans) {
        printf("%d\n", a);
    }

    return 0;
}

歡迎關注我的個人博客以閱讀更多優秀文章:凝神長老和他的朋友們(https://www.jxtxzzw.com)

也歡迎關注我的其他平臺:知乎( https://s.zzw.ink/zhihu )、知乎專欄( https://s.zzw.ink/zhuanlan )、嗶哩嗶哩( https://s.zzw.ink/blbl )、微信公衆號( 凝神長老和他的朋友們 )
凝神長老的二維碼們

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章