ZJOI2013 K大數查詢

2022-07-26 12:30:25 字數 1748 閱讀 6714

題目鏈結

首先另 l = -n, r = n,那麼 mid = (l + r) >> 1.考慮對於 mid 把所有操作(修改 / 查詢)分為兩類:dl 和 dr,使得兩部分互不干擾。這樣就可以把原問題拆成兩個子問題,從而進行整體二分。

考慮維護乙個線段樹,表示 [l,r] 區間有多少個數 > mid;

對於修改操作:

如果 c <= mid,該操作可能對左區間產生貢獻,因此放入 dl 內。

對於查詢操作:

如果 c > res,說明該詢問的答案應該 < mid,因此把當前詢問分入 dl 內;

否則,說明當前詢問的答案應該 >= mid,因此把當前詢問放入 dr 內。

總結一下,線段樹需要支援如下兩個操作:區間 +1,區間求和。

其他一些具體細節在**裡(**是抄的題解 qwq)

#include #include #include #include #define ll long long

using namespace std;

const ll n = 2333333;

struct que q[n], dl[n], dr[n];

ll n, m, num = 0, len1 = 0, len2 = 0, ql[n], qr[n], ans[n];

struct segmenttree

void push_down(ll x, ll l, ll r)

void update(ll x, ll l, ll r, ll stdl, ll stdr, ll k)

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

push_down(x, l, r);

update(x << 1, l, mid, stdl, stdr, k);

update(x << 1 | 1, mid + 1, r, stdl, stdr, k);

push_up(x), push_down(x, l, r);

}ll query(ll x, ll l, ll r, ll stdl, ll stdr)

} tree;

void work(ll st, ll ed, ll l, ll r)

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

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

else dl[++lenl] = q[i];

else}}

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

if(q[i].opt == 1 && q[i].c > mid)

tree.update(1, 1, n, q[i].l, q[i].r, -1);

for(ll i = 1; i <= lenl; i++) q[st + i - 1] = dl[i];

for(ll i = lenl + 1; i <= lenl + lenr; i++) q[st + i - 1] = dr[i - lenl];

if(fl == 1) work(st, st + lenl - 1, l, mid);

if(fr == 1) work(st + lenl, ed, mid + 1, r);

} int main()

work(1, m, -n, n);

for(ll i = 1; i <= num; i++) printf("%lld\n", ans[i]);

return 0;

}

ZJOI2013 K大數查詢

有n個位置,m個操作。1 a b c形式,表示在第a個位置到第b個位置,每個位置加入乙個數c 2 a b c形式,表示詢問從第a個位置到第b個位置,第c大的數是多少。區間的第k大值有一種二分的做法。二分答案mid,計算出區間內 mid的值有多少個。若數量小於c,則ans mid,否則ans mid。...

ZJOI2013 K大數查詢

有n個位置,m個操作。操作有兩種,每次操作如果是 2 a b c 表示詢問從第a個位置到第b個位置,第c大的數是多少。輸入格式 第一行n,m接下來m行,每行形如1 a b c或2 a b c 輸出格式 輸出每個詢問的結果 solution 整體二分。假設我們現在要解決 ql,qr 並且他們的答案 加...

ZJOI2013 K大數查詢

點此看題 0x01 樹套樹 這道題的思路特別巧妙,樹套樹不一定要用區間線段樹套權值線段樹,還可以反過來套。我們維護乙個動態開點的權值線段樹,每個點代表權值 l,r l,r l,r 在整個區間的出現情況,套上乙個動態開點的區間線段樹,操作1 11對權值線段樹單點修改,然後對每個點的 a,b a,b a...