URL : https://www.luogu.org/problem/P3369
질문의 의미 :
다음 함수 각각 $ O (logn) 내의 데이터 구조 (1 \ 당량의 N \의 당량의 1E6) $의 제조 :
순서 번호를 삭제 두; 셋째, 큰 $ k에 $으로 찾을 넷째, 심문 $ $ K 다수; A는, 일련 번호에 삽입되어 V를 X $ $는 전구체 여섯 발견 $ X의 $ 후계자를 찾을 수 있습니다.
해결 방법 :
물론, 이진 검색 트리가이 작업을 수행 할 수 있지만, 최악의 경우, 이진 트리가 체인으로 변질 것, 그것은 시간 초과, 그래서 우리가 쓰기 더 쉽게 균형 이진 트리가 필요, 빠른 속도는 treap입니다. treap 비 스핀으로 나누어 treap treap 스핀, 전자는 빠르지 만 느린 지속성,하지만 지속적인 지원을 지원하지 않습니다.
첫째, 스핀 treap있다
Treap 오른쪽 노드는이 번호는 각 노드에 대응하는 임의의 수이고,는이 스택을 유지하기 위해 부가적인 특성들에 의해 균형 것이 필요하고, 균형을 위해, 진 본성의 값을 유지한다. 회전 난수 회전 할 때와 같이, 좌측 및 우측으로 분할 :
(참고 블로그 : https://blog.csdn.net/K346K346/article/details/50808879 )
왼쪽 및 오른쪽
물론 오른쪽 서브 트리, 오른 손잡이 아이가 루트가 원래 나무 뿌리의 왼쪽, 왼쪽 서브 트리, L 공감의 원래 루트로 원래의 왼쪽 서브 트리의 루트입니다. 이진 트리를 회전 한 후 변화의 본질을 증명하기 쉽다.
삽입 할 때, 먼저 삽입 지점을 찾은 다음 다시 회전.
더 복잡한 제거는 삭제 찾아 삭제 후 하위 트리의 루트로하는 서브 트리를 관찰 임의의 값을 선택 포인트를 삭제 한 다음 노드의 회전에 의해 잎을 제거 할 수있는 이동 후 삭제 될 수 있습니다.
쉽게 쿼리 작업은 코드를보고, 이해합니다.
AC 코드 :
#INCLUDE <비트 / stdc ++ H.> 네임 스페이스를 사용하여 표준; CONST의 INT의 MAXN = 100005; INF = CONST INT 0x3f3f3f3f; 구조체 Treap { 구조체 노드 { INT 계산해 RND, LC, RC, 크기, NUM; }; INT CNT = 0; 노드 TR [MAXN]; 공극 초기화 () { CNT = 0; } INT의 _rand () { 정적 INT 시드 = 12345; 시드 = (int)에 시드를 반환 482711LL % * 2147483647; } 공극 팔 굽혀 펴기 (INT의 P) { TR [P] 크기는 TR = [그럴 [피] .lc] 크기는 + TR [TR [P] .RC] 크기는 TR + [P] .num; } 공극 오른쪽 (INT & k) { INT TMP = TR [K] .lc; TR [K] = .lc TR [TMP] .RC; TR [TMP] .RC = K; TR [TMP] 크기는 TR = [K] 크기는; 팔 굽혀 펴기 (K); k는 TMP를 =; } 공극 왼쪽 (INT & k) { INT TMP = TR [K] .RC; TR [K] = .RC TR [TMP] .lc; TR [TMP] .lc = K; TR [TMP] 크기는 TR = [K] 크기는; 팔 굽혀 펴기 (K); k는 TMP를 =; } 공극 인서트 (INT & P, INT의 X) { (p == 0) 경우에 { p = CNT ++; 그럴 [피] = .val의 X; 그럴 [피] .num TR = [P] = 크기는 1; TR [P] .lc TR = [P] .RC = 0; TR [P] = .rnd _rand (); 반환; } ++ TR [P] 크기는; (X == TR [P] .val 있으면) ++ TR [P] .num; 다른 경우 (X <TR [P] .val) { 인서트 (TR [P] .lc, X); (TR [TR [P] .lc] .rnd <TR [P] .rnd 있으면) 오른쪽 (p); } 다른 경우 (x> TR [P] .val) { 인서트 (TR [P] .RC, X); (TR [TR [P] .RC] .rnd <TR [P] .rnd 있으면) 왼쪽 (p); } } 공극 델 (INT & P, INT의 X) { 경우 (p == 0) 돌려 준다 경우 (TR [P] == .val의 X)를 { 의 경우 (TR [P] .num> 1) --tr [P] .num - TR [P] 크기는; 또 { 경우 (TR [P] .lc == 0 || TR [P] .RC == 0) p = TR [P] .lc TR + [P] .RC; 다른 경우 (TR [TR [P] .lc] .rnd <TR [그럴 [피] .RC] .rnd) 오른쪽 (p), 델 (p, X); 다른 경우 (TR [TR [P] .lc] .rnd> TR [그럴 [피] .RC] .rnd) 왼쪽 (p), 델 (p, X); } } 다른 경우 (TR의 [피] .val <x) --tr [P] 크기는, 델 (TR [P] .RC, X); 다른 --tr [P] 크기는, 델 (TR [P] .lc, X); } INT queryrnk (INT & P, INT의 X) { 경우 (p == 0) 창 0; 다른 경우 (TR [P] == .val의 X) TR [TR [P]를 .lc] + 리턴 크기는 1; (TR의 [p를] .val <x) 다른 경우 TR [TR [P]를 .lc] 복귀 크기는 TR + [P] + .num queryrnk (TR [P] .RC, X); 다른 리턴 queryrnk (TR [P] .lc, X); } INT querynum (INT & P, INT RNK) { 경우 (p == 0) 복귀 0; 경우 (TR [TR [P] .lc] 크기는> = RNK) 복귀 querynum (TR [P] .lc, RNK); rnk- = TR [그럴 [피] .lc] 크기는; 경우 (RNK <TR = [P] .num) 복귀 그럴 [피] .val; rnk- TR = [P] .num; querynum (TR [P] .RC, RNK)을 반환; } INT queryfront (INT & P, INT (X)의) { (p == 0)의 경우 tr.init (); -inf 반환; 경우 (TR의 [피] .val <x) 복귀 맥스 (TR [P] .val, queryfront (TR [P] .RC, X)); 다른 경우 (TR의 [피] .val> = x)를 리턴 queryfront (TR [P] .lc, X); } INT queryback (INT & P, INT의 X) { 경우 (p == 0) 복귀 INF 단계; 경우 (TR의 [피] .val> X) 분을 반환 (TR [P] .val, queryback (TR [P] .lc, X)); 다른 경우 (TR [P] .val <= x)를 리턴 queryback (TR [P] .RC, X); } }; INT의 POS; Treap의 TR; INT의 main () { INT 않음; scanf와 ( "%의 D", N); INT의 m, K; (0 = 1을 나타내는 int i가 N <; I ++) { scanf와 ( "%의 D % d에", m, k)를; 경우 (m == 1) tr.insert (POS, K); 그렇지 않은 경우 (m을 == 2) 만약 tr.del (POS, K); 다른 경우 (m을 == 3) 의 printf ( "% D \ 없음"tr.queryrnk (POS, K)); 다른 경우 (m을 == 4) 의 printf ( "% D \ 없음"tr.querynum (POS, K)); 다른 경우 (m을 == 5) 의 printf ( "% D \ 없음"tr.queryfront (POS, K)); 다른 경우 (m을 == 6) 의 printf ( "% D \ 없음"tr.queryback (POS, K)); } 0을 반환; }