1. 程式人生 > >LOJ #6270. 數據結構板子題 (離線+樹狀數組)

LOJ #6270. 數據結構板子題 (離線+樹狀數組)

pac 存在 AI 三種 出了 n) In 端點 表示

  • 題意

\(n\) 個區間,第 \(i\) 個區間是 \([l_i,r_i]\) ,它的長度是 \(r_i-l_i\)

\(q\) 個詢問,每個詢問給定 \(L,R,K\) ,詢問被 \([L,R]\) 包含的且長度不小於 \(K\) 的區間數量。

\(n,q≤500,000\)

  • 題解 :

想了無數種 \(O((n+q) \log^2 n)\) 的做法啊TAT 後來看了 這份代碼 後恍然大悟 .

這題一個很顯然的想法是離線 qwq

首先離線 \(l ~or~ r\) 似乎不太可行 , 因為要動態支持查找一個區間 \([L, R]\) 不小於 \(K\) 的個數 , 而主席樹需要離線完成 (劃分樹沒學過 , 不知道可不可以) 在線的話只有 \(O(\log^2)\)

的復雜度可行了 .

那繼續考慮離線 \(K\) , 然後這樣的話 , 我們就可以忽略 \(K\) 的限制 .

假設我們當前算出了對於一個詢問 所有長度 \(\le k\) 的區間個數 \(res_k\) , 那這個答案就可以表示成 \(res_n - res_{K-1}\) .

我們此時只需要做的就是 計算一個區間包含了當前的多少個區間 .

這個如何做呢 qwq

兩個區間 \(A, B\) 只有三種情況 .

  1. \(A \in B\) (此時 \(A\) 可以等於 \(B\) ) , \(B\) 完全包含 \(A\) , \(|B| \ge |A|\)
  2. \(A \not \in B\)\(B \not \in A\)
    , 此時 \(A,B\) 交的部分不會是這兩個區間中任意一個全集 .
  3. \(B \in A ~(A\not = B)\) , \(A\) 完全包含 \(B\) , \(|A| > |B|\)

我們假設前面插入的區間為 \(A\) , 當前詢問的區間為 \(B\) . 我們計算的就只有第 \(1\) 種情況了 .

不難發現 第 \(3\) 情況計數很麻煩 .

我們最好忽略第 \(3\) 種情況 , 只計算第 \(2\) 種情況 . (因為這個 \(A\) 會有兩個出去不好計算)

不難發現長度會有限制 , 那我們詢問的時候 只要詢問所有 \(len \le R - L\) 的區間就行了 , 也就是每個離線後變成計算 \(res_{R-L} - res_{K - 1}\)

.

而對於第 \(2\) 種情況 , \(A\) 只有一端會超出 \(B\) .

然後每次詢問 \(res_i\) 的時候 假設當前插入的線段的總數是 \(tot\) .

答案顯然就是 $tot - $ 前面左端點存在於 \([1, L - 1]\) 的線段個數 - 前面右端點存在於 \([R + 1, n]\) 的線段個數 .

(可以發現 , 這樣很好地處理了兩個線段相離的情況)

然後這個用兩個樹狀數組統計一下 , 左端點和右端點各一個 .

所以最後的時間復雜度就是 \(O((n + q) \log n)\) 了 .

註意一開始離線的時候 要特判掉 \(R-L < K\) 的情況 !!

最好自己畫圖理解 , 博主懶就沒放上來了qwq

  • 代碼 :
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define debug(x) cout << #x << ‘:‘ << x << endl
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == ‘-‘) fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
    freopen ("6270.in", "r", stdin);
    freopen ("6270.out", "w", stdout);
#endif
}

const int N = 500100;

int n, q;

#define lowbit(x) (x & -x)
struct Fenwick_Tree {
    int sumv[N];

    inline void Update(int pos) { for(; pos <= n; pos += lowbit(pos)) ++ sumv[pos]; }

    inline int Query(int pos) { int res = 0; for (; pos > 0; pos ^= lowbit(pos)) res += sumv[pos]; return res; }
} Pre, Suf;

struct Ask { int opt, l, r, id; } ;
typedef pair<int, int> PII;
#define fir first
#define sec second

vector<Ask> Q[N]; vector<PII> V[N]; int ans[N];

#define Rev(x) (n - (x) + 1)
int main () {
    File();
    n = read(); q = read();

    For (i, 1, n) {
        int l = read(), r = read(), len = r - l;
        V[len].push_back(make_pair(l, r));
    }

    For (i, 1, q) {
        int l = read(), r = read(), k = read(), len = r - l;
        if (len >= k)
            Q[k - 1].push_back((Ask) {-1, l, r, i}),
            Q[len].push_back((Ask) {1, l, r, i});
    }

    int tot = 0;
    For (i, 1, n) {
        for (PII Up : V[i])
            Pre.Update(Up.fir), Suf.Update(Rev(Up.sec)), ++ tot;
        for (Ask Rp : Q[i])
            ans[Rp.id] += Rp.opt * (tot - Pre.Query(Rp.l - 1) - Suf.Query(Rev(Rp.r + 1)));
    }

    For (i, 1, q) printf ("%d\n", ans[i]);

    return 0;
}

LOJ #6270. 數據結構板子題 (離線+樹狀數組)