題目鏈結
首先另 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...