靜態區間第K小(整體二分 主席樹)

2022-05-08 17:42:10 字數 1661 閱讀 8080

題目鏈結

題解主席樹入門題

但是這裡給出整體二分解法

整體二分顧名思義是把所有操作放在一起二分

想想,如果求\([1-n]\)的第\(k\)小怎麼二分求得?

我們可以二分答案\(k\), \(o(n)\)統計有多少個數小於等於\(k\)

如果對於每個詢問都這麼搞,肯定不行

我們可以發現,如果每次都搞一次,有許多算重複的地方

\(div(l, r, st, ed)\)表示\(k\)二分的區間\([l-r]\), 對應操作答案區間在\([st-ed]\)

(如果沒看懂,先往下看。)

\(mid = (l+r)/2\)

對於每次的\(mid\), 我們把對\(k\)往左移還有貢獻的 放在一起, 右移的放在一起,這樣答案就在乙個區間內

那麼如何統計一段區間有多少個數比\(k\)大呢?

我們開乙個樹狀陣列,如果\(x\)位上的數大於\(k\),那麼給\(x\)加上1

然後統計就是區間求和了

code

#include#define ll long long

#define rg register

using namespace std;

const int n = 200010, inf = 1e9;

struct rec q[n<<1], lq[n<<1], rq[n<<1];

int n, m, tot, c[n], ans[n];

#define lowbit(x) (x&(-x))

void add(int x, int v)

int sum(int x)

int x[n], cnt;

inline int gi()

void div(int l, int r, int st, int ed)

int mid = (l + r) >> 1;

int lt = 0, rt = 0;

for (int i = st; i <= ed; i++)

else

} for (int i = st; i <= ed; i++)

if (!q[i].op && q[i].y <= mid) add(q[i].x, -1);//清除

for (int i = 1; i <= lt; i++) q[st+i-1] = lq[i];

for (int i = 1; i <= rt; i++) q[st+lt+i-1] = rq[i];

div(l, mid, st, st+lt-1);

div(mid+1, r, st+lt, ed);

return ;

}int main()

sort(x+1, x+1+cnt); cnt = unique(x+1, x+cnt+1)-x-1;//離散化

for (int i = 1; i <= n; i++)

q[i].y = lower_bound(x+1, x+cnt+1, q[i].y)-x;

for (int i = 1; i <= m; i++)

div(1, n, 1, tot);

for (int i = 1; i <= m; i++)

printf("%d\n", x[ans[i]]);

return 0;

}

主席樹 靜態區間第k小

這是個非常經典的主席樹入門題 靜態區間第k小 資料已經過加強,請使用主席樹。同時請注意常數優化 如題,給定n個整數構成的序列,將對於指定的閉區間查詢其區間內的第k小值。輸入格式 第一行包含兩個正整數n m,分別表示序列的長度和查詢的個數。第二行包含n個整數,表示這個序列各項的數字。接下來m行每行包含...

主席樹(區間第k小)

k th number 求區間內第k小的數。主席樹的板子題 主席樹左子樹存小值,右邊大值,用sum記錄一下子樹節點個數。對 l,r 的查詢區間,root r root l 1 可得出 l,r 的差值,也就是大小的個數 include include include include include i...

主席樹 區間第k小

主席樹 權值線段樹 可持久化 權值線段樹 在此處指各個數字在某個區間內出現的次數 那麼第一棵權值線段樹會記錄 1,1 的數字出現次數 第n棵權值線段樹會記錄 1,n 的數字出現次數 例 數列為110001 第一棵權值線段樹記錄為tree1 0 0 tree1 1 1 第二棵權值線段樹記錄為tree2...