有n個位置,m個操作。
1 a b c形式,表示在第a個位置到第b個位置,每個位置加入乙個數c
2 a b c形式,表示詢問從第a個位置到第b個位置,第c大的數是多少。
區間的第k大值有一種二分的做法。
二分答案mid,計算出區間內》mid的值有多少個。
若數量小於c,則ans<mid,否則ans>=mid。
考慮離線做法,將所有詢問一起二分。
每次二分掃瞄所有操作,維護乙個線段樹記錄在一段區間內有多少個》mid的數。
對於乙個詢問,直接查詢在區間內的》a的數量即可知道ans<mid或》=mid。
但是運算元量也很多,如果每次二分掃瞄所有操作也會超時。
操作也可以分治,根據c與mid的大小關係進行分治。將操作序列整體二分。
把c<=mid的修改放到左邊,反之放在右邊。
將ans<mid的的詢問放到左邊,反之放到右邊。
實現時,若用樹狀陣列,清空是把加上去的減回來;若用線段樹,打標記清空。
#include
#include
using
namespace std;
typedef
long
long ll;
struct node qu[
50005
],q1[
50005
],q2[
50005];
int ans[
50005
],cnt;
ll c1[
50005
],c2[
50005];
int n,m;
inline ll read()
inline
intlowbit
(int x)
inline
void
update
(int x,ll val)
inline ll getsum
(int x)
inline
void
update
(int l,
int r,ll val)
inline ll query
(int l,
int r)
void
solve
(int l,
int r,
int s,
int t)
int mid=
(l+r)
>>1;
int s1=
0,s2=0;
for(
int i=s;i<=t;i++
)else q1[
++s1]
=qu[i];}
else
else q2[
++s2]
=qu[i];}
}for
(int i=s;i<=t;i++
)for
(int i=
1;i<=s1;i++
) qu[s+i-1]
=q1[i]
;for
(int i=
1;i<=s2;i++
) qu[t-s2+i]
=q2[i]
;solve
(l,mid,s,s+s1-1)
;solve
(mid+
1,r,s+s1,t);}
intmain()
solve
(mn,mx,
1,m)
;for
(int i=
1;i<=cnt;i++
)printf
("%d\n"
,ans[i]);
return0;
}
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...
ZJOI2013 K大數查詢
顯然可以線段樹套線段樹。但是必須卡常。所以我採用了整體二分。注意是區間第k大,而不是第k小。然後區間轉化一下即可。有乙個剪枝的地方,就是如果這個區間已經沒有查詢的操作了,那就不用遞迴了。ac pragma gcc optimize ofast funroll all loops include de...