題解/算法 {\2476. 树套树}
@LINK: https://www.acwing.com/problem/content/description/2478/
;
比如[2,3,3,5]
, 對於操作2, 排名1為2
排名2/3為3
排名4為5
; 對於操作1, 2的排名為1
3的排名為2
5的排名為4
;
線段樹套Splay; 注意 對於操作3/4/5 都不要調用Update_bySon
(會超時), 需要進行優化成log;
操作1: x的排名 定義為<x的個數 + 1
, 因此 這符合累加性 (即各個子區間的<x的個數
之和 就是整體的答案);
但是對於操作2: 求排名為K的數, 這是無法通過線段樹來求的(即各個子區間的第K小數 並不是整體的第K小數); 正確做法是通過轉換為操作1來二分, 比如[3, 5,5, 7]
求排名為2的數, 那麼反過來: 任意<=3
的數 排名是1
, [4,5]
的排名是2
, [6,7]
的排名是4
, 任意>7
的數 排名是5
; 我們的答案是5
(即排名為2的是5), 找排名<=K
的最大數;
__RetType_Query_ Get_intervalData_rank( int _l, int _r, int _x){ // 操作1
ASSERT_WEAK_( 0<=_l && _l<=_r && _r<__Range);
function<__RetType_Query_(__Node_*)> dfs = [&]( __Node_ * _cur){
_cur->UpdateSon_byLazy();
if( _l<=_cur->Interval.first && _r>=_cur->Interval.second){ // `cur`不一定是*葉子節點*;
return _cur->Data.Tr.Get_elementsCount_byLessValue( _x);
}
int mid = (_cur->Interval.first+_cur->Interval.second)>>1;
if( _r <= mid){ return dfs( _cur->SonPtr[0]);} else if( _l > mid){ return dfs( _cur->SonPtr[1]);}
auto lef = dfs( _cur->SonPtr[0]), rig = dfs( _cur->SonPtr[1]);
return lef + rig;
};
return dfs( __Root) + 1;
}
__RetType_Query_ Get_intervalData_getK( int _l, int _r, int _k){ // 操作2
ASSERT_WEAK_( 0<=_l && _l<=_r && _r<__Range);
int l = 0, r = 1e8;
while( l < r){
auto mid = (l+r+1)>>1;
if( Get_intervalData_rank(_l, _r, mid) <= _k){ l = mid;}
else{ r = mid - 1;}
}
return r;
}