快轉到主要內容

低地距離

目錄

題目連結

簡易題敘
#

輸入一個長度為 \(2n\) 的陣列 ( \(1 \le n \le 10^5\) ),其中 \(1\) 到 \(n\) 的每個數字都剛好各 2 次。 \(i\) 的低窪值的定義是兩個數值為 \(i\) 的位置中間,有幾個小於 \(i\) 的的數字。
以 [3, 1, 2, 1, 3, 2] 為例,1 的低窪值為 0, 2 的低窪值值為 1, 3 的低窪值為 3。
請對於每個 \(1\) 到 \(n\) 的數字都求其低窪值(兩個相同的數字之間有幾個數字比它小),輸出低窪值的總和,答案可能會超過 C++ int 的上限。

範例測資:

  • 輸入
3
3 1 2 1 3 2
  • 輸出
4

概念講解
#

這題我們可以嘗試用 BIT 來解。如果對 BIT 概念不熟的話,可以先看看這篇文章

因為低窪值的定義是兩數間比它小的數的數量,所以我們可以透過事先記錄每個數字出現的位置,再從小的數字開始在這些紀錄好的位置上做記號:

    for (int i = 1, t; i <= 2 * n; i++) {
        cin >> t;
        if (pos[t].first == 0) {
            pos[t].first = i;
        } else {
            pos[t].second = i;
        }
    }

這樣一來,兩數之間的記號都是比它小的數字打上去的,也就是說,只要知道兩數間記號的數量,就等於知道了低窪值。而這樣需要單點加值 ( 在紀錄好的位置上做記號 )、區間求和 ( 求兩數間記號數量 ) 的特性,正符合 BIT 支援的功能。每次總低窪值都要加上兩數中間 ( 不含兩端!!! ) 的記號數:

        ans += sum(pos[i].second - 1) - sum(pos[i].first); // pos[i].second - 1 要特別注意,不要忘記兩數不包含在低窪裡

算完之後不要忘記幫兩端打上記號:

        add(pos[i].first);
        add(pos[i].second);

範例程式碼
#

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 2e5 + 5;
pii pos[N];
int n, ans, bit[N];

void add(int id) {
    for (int i = id; i <= 2 * n; i += i & -i) {
        bit[i]++;
    }
}

int sum(int id) {
    int ans = 0;
    for (int i = id; i > 0; i -= i & -i) {
        ans += bit[i];
    }
    return ans;
}

signed main() {
    cin >> n;
    for (int i = 1, t; i <= 2 * n; i++) {
        cin >> t;
        if (pos[t].first == 0) {
            pos[t].first = i;
        } else {
            pos[t].second = i;
        }
    }

    for (int i = 1; i <= n; i++) {
        ans += sum(pos[i].second - 1) - sum(pos[i].first);
        add(pos[i].first);
        add(pos[i].second);
    }

    cout << ans;
    return 0;
}
Piau 的筆記本
作者
Piau 的筆記本
希望我寫下來的東西能夠長久的記在我的腦中